коммит произвёл
GitHub
родитель
8c4ed0b65b
Коммит
24a90356e6
@@ -40,6 +40,14 @@ func (a *App) SlackImport(c request.CTX, fileData multipart.File, fileSize int64
|
|||||||
GeneratePreviewImage: a.generatePreviewImage,
|
GeneratePreviewImage: a.generatePreviewImage,
|
||||||
InvalidateAllCaches: func() *model.AppError { return a.ch.srv.platform.InvalidateAllCaches() },
|
InvalidateAllCaches: func() *model.AppError { return a.ch.srv.platform.InvalidateAllCaches() },
|
||||||
MaxPostSize: func() int { return a.ch.srv.platform.MaxPostSize() },
|
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) {
|
PrepareImage: func(fileData []byte) (image.Image, string, func(), error) {
|
||||||
img, imgType, release, err := prepareImage(c, a.ch.imgDecoder, bytes.NewReader(fileData))
|
img, imgType, release, err := prepareImage(c, a.ch.imgDecoder, bytes.NewReader(fileData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3031,8 +3031,8 @@
|
|||||||
"translation": "Could not uninvite remote to channel"
|
"translation": "Could not uninvite remote to channel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.slackimport.slack_add_bot_user.email_pwd",
|
"id": "api.slackimport.slack_add_bot_user.email",
|
||||||
"translation": "The Integration/Slack Bot user with email {{.Email}} and password {{.Password}} has been imported.\r\n"
|
"translation": "The Integration/Slack Bot user with email {{.Email}} has been imported.\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.slackimport.slack_add_bot_user.unable_import",
|
"id": "api.slackimport.slack_add_bot_user.unable_import",
|
||||||
@@ -3059,8 +3059,8 @@
|
|||||||
"translation": "\r\nUsers created:\r\n"
|
"translation": "\r\nUsers created:\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.slackimport.slack_add_users.email_pwd",
|
"id": "api.slackimport.slack_add_users.email",
|
||||||
"translation": "Slack user with email {{.Email}} and password {{.Password}} has been imported.\r\n"
|
"translation": "Slack user with email {{.Email}} has been imported.\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.slackimport.slack_add_users.merge_existing",
|
"id": "api.slackimport.slack_add_users.merge_existing",
|
||||||
@@ -3074,6 +3074,10 @@
|
|||||||
"id": "api.slackimport.slack_add_users.missing_email_address",
|
"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"
|
"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",
|
"id": "api.slackimport.slack_add_users.unable_import",
|
||||||
"translation": "Unable to import Slack user: {{.Username}}.\r\n"
|
"translation": "Unable to import Slack user: {{.Username}}.\r\n"
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ type Actions struct {
|
|||||||
GeneratePreviewImage func(request.CTX, image.Image, string, string)
|
GeneratePreviewImage func(request.CTX, image.Image, string, string)
|
||||||
InvalidateAllCaches func() *model.AppError
|
InvalidateAllCaches func() *model.AppError
|
||||||
MaxPostSize func() int
|
MaxPostSize func() int
|
||||||
|
SendPasswordReset func(string) (bool, *model.AppError)
|
||||||
PrepareImage func(fileData []byte) (image.Image, string, func(), error)
|
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))
|
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
|
// Check for email conflict and use existing user if found
|
||||||
if existingUser, err := si.store.User().GetByEmail(email); err == nil {
|
if existingUser, err := si.store.User().GetByEmail(email); err == nil {
|
||||||
addedUsers[sUser.Id] = existingUser
|
addedUsers[sUser.Id] = existingUser
|
||||||
@@ -287,7 +286,7 @@ func (si *SlackImporter) slackAddUsers(rctx request.CTX, teamId string, slackuse
|
|||||||
FirstName: firstName,
|
FirstName: firstName,
|
||||||
LastName: lastName,
|
LastName: lastName,
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: password,
|
Password: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
mUser := si.oldImportUser(rctx, team, &newUser)
|
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}))
|
importerLog.WriteString(i18n.T("api.slackimport.slack_add_users.unable_import", map[string]any{"Username": sUser.Username}))
|
||||||
continue
|
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
|
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
|
return addedUsers
|
||||||
@@ -327,7 +336,7 @@ func (si *SlackImporter) slackAddBotUser(rctx request.CTX, teamId string, log *b
|
|||||||
return nil
|
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
|
return mUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package slackimport
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -760,3 +761,114 @@ func TestSlackImportEnhancedSecurityBackwardsCompatibility(t *testing.T) {
|
|||||||
// Verify VerifyEmail was NOT called
|
// Verify VerifyEmail was NOT called
|
||||||
userStore.AssertNotCalled(t, "VerifyEmail")
|
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")
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user