Automatic Merge
Этот коммит содержится в:
Mattermost Build
2026-03-18 14:30:54 +01:00
коммит произвёл GitHub
родитель 8c4ed0b65b
Коммит 24a90356e6
4 изменённых файлов: 142 добавлений и 9 удалений

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

@@ -40,6 +40,14 @@ func (a *App) SlackImport(c request.CTX, fileData multipart.File, fileSize int64
GeneratePreviewImage: a.generatePreviewImage,
InvalidateAllCaches: func() *model.AppError { return a.ch.srv.platform.InvalidateAllCaches() },
MaxPostSize: func() int { return a.ch.srv.platform.MaxPostSize() },
SendPasswordReset: func(email string) (bool, *model.AppError) {
sent, err := a.SendPasswordReset(c, email, a.GetSiteURL())
if err != nil {
return false, err
}
return sent, nil
},
PrepareImage: func(fileData []byte) (image.Image, string, func(), error) {
img, imgType, release, err := prepareImage(c, a.ch.imgDecoder, bytes.NewReader(fileData))
if err != nil {

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

@@ -3031,8 +3031,8 @@
"translation": "Could not uninvite remote to channel"
},
{
"id": "api.slackimport.slack_add_bot_user.email_pwd",
"translation": "The Integration/Slack Bot user with email {{.Email}} and password {{.Password}} has been imported.\r\n"
"id": "api.slackimport.slack_add_bot_user.email",
"translation": "The Integration/Slack Bot user with email {{.Email}} has been imported.\r\n"
},
{
"id": "api.slackimport.slack_add_bot_user.unable_import",
@@ -3059,8 +3059,8 @@
"translation": "\r\nUsers created:\r\n"
},
{
"id": "api.slackimport.slack_add_users.email_pwd",
"translation": "Slack user with email {{.Email}} and password {{.Password}} has been imported.\r\n"
"id": "api.slackimport.slack_add_users.email",
"translation": "Slack user with email {{.Email}} has been imported.\r\n"
},
{
"id": "api.slackimport.slack_add_users.merge_existing",
@@ -3074,6 +3074,10 @@
"id": "api.slackimport.slack_add_users.missing_email_address",
"translation": "User {{.Username}} does not have an email address in the Slack export. Used {{.Email}} as a placeholder. The user should update their email address once logged in to the system.\r\n"
},
{
"id": "api.slackimport.slack_add_users.send_reset_email_failed",
"translation": "Unable to send password reset email to {{.Username}} at {{.Email}}.\r\n"
},
{
"id": "api.slackimport.slack_add_users.unable_import",
"translation": "Unable to import Slack user: {{.Username}}.\r\n"

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

@@ -95,6 +95,7 @@ type Actions struct {
GeneratePreviewImage func(request.CTX, image.Image, string, string)
InvalidateAllCaches func() *model.AppError
MaxPostSize func() int
SendPasswordReset func(string) (bool, *model.AppError)
PrepareImage func(fileData []byte) (image.Image, string, func(), error)
}
@@ -268,8 +269,6 @@ func (si *SlackImporter) slackAddUsers(rctx request.CTX, teamId string, slackuse
rctx.Logger().Warn("Slack Import: User does not have an email address in the Slack export. Used username as a placeholder. The user should update their email address once logged in to the system.", mlog.String("user_email", email), mlog.String("user_name", sUser.Username))
}
password := model.NewId()
// Check for email conflict and use existing user if found
if existingUser, err := si.store.User().GetByEmail(email); err == nil {
addedUsers[sUser.Id] = existingUser
@@ -287,7 +286,7 @@ func (si *SlackImporter) slackAddUsers(rctx request.CTX, teamId string, slackuse
FirstName: firstName,
LastName: lastName,
Email: email,
Password: password,
Password: "",
}
mUser := si.oldImportUser(rctx, team, &newUser)
@@ -295,8 +294,18 @@ func (si *SlackImporter) slackAddUsers(rctx request.CTX, teamId string, slackuse
importerLog.WriteString(i18n.T("api.slackimport.slack_add_users.unable_import", map[string]any{"Username": sUser.Username}))
continue
}
sent, err := si.actions.SendPasswordReset(email)
if err != nil {
rctx.Logger().Warn("Slack Import: Cannot send password reset email to user. An admin should update their email address once logged in to the system.", mlog.String("user_email", email), mlog.String("user_name", sUser.Username))
}
if !sent {
importerLog.WriteString(i18n.T("api.slackimport.slack_add_users.send_reset_email_failed", map[string]any{"Username": sUser.Username, "Email": newUser.Email}))
}
addedUsers[sUser.Id] = mUser
importerLog.WriteString(i18n.T("api.slackimport.slack_add_users.email_pwd", map[string]any{"Email": newUser.Email, "Password": password}))
importerLog.WriteString(i18n.T("api.slackimport.slack_add_users.email", map[string]any{"Email": newUser.Email}))
}
return addedUsers
@@ -327,7 +336,7 @@ func (si *SlackImporter) slackAddBotUser(rctx request.CTX, teamId string, log *b
return nil
}
log.WriteString(i18n.T("api.slackimport.slack_add_bot_user.email_pwd", map[string]any{"Email": botUser.Email, "Password": password}))
log.WriteString(i18n.T("api.slackimport.slack_add_bot_user.email", map[string]any{"Email": botUser.Email}))
return mUser
}

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

@@ -6,6 +6,7 @@ package slackimport
import (
"archive/zip"
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
@@ -760,3 +761,114 @@ func TestSlackImportEnhancedSecurityBackwardsCompatibility(t *testing.T) {
// Verify VerifyEmail was NOT called
userStore.AssertNotCalled(t, "VerifyEmail")
}
type slackAddUsersTestSetup struct {
store *mocks.Store
teamStore *mocks.TeamStore
userStore *mocks.UserStore
team *model.Team
savedUser *model.User
}
func newSlackAddUsersTestSetup(t *testing.T) *slackAddUsersTestSetup {
t.Helper()
s := &slackAddUsersTestSetup{}
s.store = &mocks.Store{}
s.teamStore = &mocks.TeamStore{}
s.userStore = &mocks.UserStore{}
s.store.On("Team").Return(s.teamStore)
s.store.On("User").Return(s.userStore)
s.team = &model.Team{Id: "test-team-id", Name: "test-team"}
s.teamStore.On("Get", "test-team-id").Return(s.team, nil)
s.userStore.On("GetByEmail", mock.AnythingOfType("string")).Return(nil, fmt.Errorf("not found"))
s.savedUser = &model.User{Id: "test-user-id", Username: "testuser", Email: "testuser@example.com"}
s.userStore.On("Save", mock.AnythingOfType("*request.Context"), mock.AnythingOfType("*model.User")).Return(s.savedUser, nil)
return s
}
func (s *slackAddUsersTestSetup) newImporter(actions Actions) *SlackImporter {
config := &model.Config{}
config.SetDefaults()
return New(s.store, actions, config)
}
func (s *slackAddUsersTestSetup) defaultActions() Actions {
return Actions{
JoinUserToTeam: func(team *model.Team, user *model.User, userRequestorId string) (*model.TeamMember, *model.AppError) {
return &model.TeamMember{}, nil
},
SendPasswordReset: func(email string) (bool, *model.AppError) {
return true, nil
},
}
}
func defaultSlackUsers() []slackUser {
return []slackUser{
{Id: "U001", Username: "testuser", Profile: slackProfile{FirstName: "Test", LastName: "User", Email: "testuser@example.com"}},
}
}
func TestSlackAddUsersLogContainsProperUserCreationMessage(t *testing.T) {
rctx := request.TestContext(t)
s := newSlackAddUsersTestSetup(t)
importer := s.newImporter(s.defaultActions())
importerLog := new(bytes.Buffer)
importer.slackAddUsers(rctx, "test-team-id", defaultSlackUsers(), importerLog)
logOutput := importerLog.String()
assert.Contains(t, logOutput, "api.slackimport.slack_add_users.email", "import log should contain the user creation message")
assert.NotContains(t, logOutput, "api.slackimport.slack_add_users.email_pwd", "import log must not use the old user creation message")
}
func TestSlackAddUsersLogsSendResetEmailFailure(t *testing.T) {
rctx := request.TestContext(t)
s := newSlackAddUsersTestSetup(t)
actions := s.defaultActions()
actions.SendPasswordReset = func(email string) (bool, *model.AppError) {
return false, nil
}
importer := s.newImporter(actions)
importerLog := new(bytes.Buffer)
importer.slackAddUsers(rctx, "test-team-id", defaultSlackUsers(), importerLog)
assert.Contains(t, importerLog.String(), "api.slackimport.slack_add_users.send_reset_email_failed")
}
func TestSlackAddUsersGeneratesUserWithEmptyPassword(t *testing.T) {
rctx := request.TestContext(t)
s := newSlackAddUsersTestSetup(t)
importer := s.newImporter(s.defaultActions())
importerLog := new(bytes.Buffer)
importer.slackAddUsers(rctx, "test-team-id", defaultSlackUsers(), importerLog)
s.userStore.AssertCalled(t, "Save", mock.AnythingOfType("*request.Context"), mock.MatchedBy(func(u *model.User) bool {
return u.Password == ""
}))
}
func TestSlackAddUsersTriggersPasswordResetFlow(t *testing.T) {
rctx := request.TestContext(t)
s := newSlackAddUsersTestSetup(t)
passwordResetCalled := false
actions := s.defaultActions()
actions.SendPasswordReset = func(email string) (bool, *model.AppError) {
passwordResetCalled = true
return true, nil
}
importer := s.newImporter(actions)
importerLog := new(bytes.Buffer)
importer.slackAddUsers(rctx, "test-team-id", defaultSlackUsers(), importerLog)
assert.True(t, passwordResetCalled, "SendPasswordReset should be called for each imported user")
}