1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package api4
5
6import (
7	"context"
8	"errors"
9	"fmt"
10	"io/ioutil"
11	"math/rand"
12	"net"
13	"net/http"
14	"os"
15	"path/filepath"
16	"strings"
17	"sync"
18	"testing"
19	"time"
20
21	s3 "github.com/minio/minio-go/v7"
22	"github.com/minio/minio-go/v7/pkg/credentials"
23	"github.com/stretchr/testify/require"
24
25	"github.com/mattermost/mattermost-server/v6/app"
26	"github.com/mattermost/mattermost-server/v6/app/request"
27	"github.com/mattermost/mattermost-server/v6/config"
28	"github.com/mattermost/mattermost-server/v6/model"
29	"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
30	"github.com/mattermost/mattermost-server/v6/services/searchengine"
31	"github.com/mattermost/mattermost-server/v6/shared/mlog"
32	"github.com/mattermost/mattermost-server/v6/store"
33	"github.com/mattermost/mattermost-server/v6/store/localcachelayer"
34	"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
35	"github.com/mattermost/mattermost-server/v6/testlib"
36	"github.com/mattermost/mattermost-server/v6/web"
37	"github.com/mattermost/mattermost-server/v6/wsapi"
38)
39
40type TestHelper struct {
41	App         *app.App
42	Server      *app.Server
43	ConfigStore *config.Store
44
45	Context              *request.Context
46	Client               *model.Client4
47	BasicUser            *model.User
48	BasicUser2           *model.User
49	TeamAdminUser        *model.User
50	BasicTeam            *model.Team
51	BasicChannel         *model.Channel
52	BasicPrivateChannel  *model.Channel
53	BasicPrivateChannel2 *model.Channel
54	BasicDeletedChannel  *model.Channel
55	BasicChannel2        *model.Channel
56	BasicPost            *model.Post
57	Group                *model.Group
58
59	SystemAdminClient *model.Client4
60	SystemAdminUser   *model.User
61	tempWorkspace     string
62
63	SystemManagerClient *model.Client4
64	SystemManagerUser   *model.User
65
66	LocalClient *model.Client4
67
68	IncludeCacheLayer bool
69
70	TestLogger *mlog.Logger
71}
72
73var mainHelper *testlib.MainHelper
74
75func SetMainHelper(mh *testlib.MainHelper) {
76	mainHelper = mh
77}
78
79func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, enterprise bool, includeCache bool,
80	updateConfig func(*model.Config), options []app.Option) *TestHelper {
81	tempWorkspace, err := ioutil.TempDir("", "apptest")
82	if err != nil {
83		panic(err)
84	}
85
86	memoryStore, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{IgnoreEnvironmentOverrides: true})
87	if err != nil {
88		panic("failed to initialize memory store: " + err.Error())
89	}
90
91	memoryConfig := &model.Config{}
92	memoryConfig.SetDefaults()
93	*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
94	*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
95	memoryConfig.ServiceSettings.EnableLocalMode = model.NewBool(true)
96	*memoryConfig.ServiceSettings.LocalModeSocketLocation = filepath.Join(tempWorkspace, "mattermost_local.sock")
97	*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
98	*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
99	*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
100	if updateConfig != nil {
101		updateConfig(memoryConfig)
102	}
103	memoryStore.Set(memoryConfig)
104
105	configStore, err := config.NewStoreFromBacking(memoryStore, nil, false)
106	if err != nil {
107		panic(err)
108	}
109
110	options = append(options, app.ConfigStore(configStore))
111	if includeCache {
112		// Adds the cache layer to the test store
113		options = append(options, app.StoreOverride(func(s *app.Server) store.Store {
114			lcl, err2 := localcachelayer.NewLocalCacheLayer(dbStore, s.Metrics, s.Cluster, s.CacheProvider)
115			if err2 != nil {
116				panic(err2)
117			}
118			return lcl
119		}))
120	} else {
121		options = append(options, app.StoreOverride(dbStore))
122	}
123
124	testLogger, _ := mlog.NewLogger()
125	logCfg, _ := config.MloggerConfigFromLoggerConfig(&memoryConfig.LogSettings, nil, config.GetLogFileLocation)
126	if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil {
127		panic("failed to configure test logger: " + errCfg.Error())
128	}
129	// lock logger config so server init cannot override it during testing.
130	testLogger.LockConfiguration()
131	options = append(options, app.SetLogger(testLogger))
132
133	s, err := app.NewServer(options...)
134	if err != nil {
135		panic(err)
136	}
137
138	th := &TestHelper{
139		App:               app.New(app.ServerConnector(s)),
140		Server:            s,
141		ConfigStore:       configStore,
142		IncludeCacheLayer: includeCache,
143		Context:           &request.Context{},
144		TestLogger:        testLogger,
145	}
146
147	if s.SearchEngine != nil && s.SearchEngine.BleveEngine != nil && searchEngine != nil {
148		searchEngine.BleveEngine = s.SearchEngine.BleveEngine
149	}
150
151	if searchEngine != nil {
152		th.App.SetSearchEngine(searchEngine)
153	}
154
155	th.App.UpdateConfig(func(cfg *model.Config) {
156		*cfg.TeamSettings.MaxUsersPerTeam = 50
157		*cfg.RateLimitSettings.Enable = false
158		*cfg.EmailSettings.SendEmailNotifications = true
159		*cfg.ServiceSettings.SiteURL = ""
160
161		// Disable sniffing, otherwise elastic client fails to connect to docker node
162		// More details: https://github.com/olivere/elastic/wiki/Sniffing
163		*cfg.ElasticsearchSettings.Sniff = false
164
165		*cfg.TeamSettings.EnableOpenServer = true
166
167		// Disable strict password requirements for test
168		*cfg.PasswordSettings.MinimumLength = 5
169		*cfg.PasswordSettings.Lowercase = false
170		*cfg.PasswordSettings.Uppercase = false
171		*cfg.PasswordSettings.Symbol = false
172		*cfg.PasswordSettings.Number = false
173
174		*cfg.ServiceSettings.ListenAddress = ":0"
175	})
176	if err := th.Server.Start(); err != nil {
177		panic(err)
178	}
179
180	Init(th.App, th.App.Srv().Router)
181	InitLocal(th.App, th.App.Srv().LocalRouter)
182	web.New(th.App, th.App.Srv().Router)
183	wsapi.Init(th.App.Srv())
184
185	if enterprise {
186		th.App.Srv().SetLicense(model.NewTestLicense())
187		th.App.Srv().Jobs.InitWorkers()
188		th.App.Srv().Jobs.InitSchedulers()
189	} else {
190		th.App.Srv().SetLicense(nil)
191	}
192
193	th.Client = th.CreateClient()
194	th.SystemAdminClient = th.CreateClient()
195	th.SystemManagerClient = th.CreateClient()
196
197	// Verify handling of the supported true/false values by randomizing on each run.
198	rand.Seed(time.Now().UTC().UnixNano())
199	trueValues := []string{"1", "t", "T", "TRUE", "true", "True"}
200	falseValues := []string{"0", "f", "F", "FALSE", "false", "False"}
201	trueString := trueValues[rand.Intn(len(trueValues))]
202	falseString := falseValues[rand.Intn(len(falseValues))]
203	mlog.Debug("Configured Client4 bool string values", mlog.String("true", trueString), mlog.String("false", falseString))
204	th.Client.SetBoolString(true, trueString)
205	th.Client.SetBoolString(false, falseString)
206
207	th.LocalClient = th.CreateLocalClient(*memoryConfig.ServiceSettings.LocalModeSocketLocation)
208
209	if th.tempWorkspace == "" {
210		th.tempWorkspace = tempWorkspace
211	}
212
213	return th
214}
215
216func SetupEnterprise(tb testing.TB) *TestHelper {
217	if testing.Short() {
218		tb.SkipNow()
219	}
220
221	if mainHelper == nil {
222		tb.SkipNow()
223	}
224
225	dbStore := mainHelper.GetStore()
226	dbStore.DropAllTables()
227	dbStore.MarkSystemRanUnitTests()
228	mainHelper.PreloadMigrations()
229	searchEngine := mainHelper.GetSearchEngine()
230	th := setupTestHelper(dbStore, searchEngine, true, true, nil, nil)
231	th.InitLogin()
232	return th
233}
234
235func Setup(tb testing.TB) *TestHelper {
236	if testing.Short() {
237		tb.SkipNow()
238	}
239
240	if mainHelper == nil {
241		tb.SkipNow()
242	}
243
244	dbStore := mainHelper.GetStore()
245	dbStore.DropAllTables()
246	dbStore.MarkSystemRanUnitTests()
247	mainHelper.PreloadMigrations()
248	searchEngine := mainHelper.GetSearchEngine()
249	th := setupTestHelper(dbStore, searchEngine, false, true, nil, nil)
250	th.InitLogin()
251	return th
252}
253
254func SetupConfig(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper {
255	if testing.Short() {
256		tb.SkipNow()
257	}
258
259	if mainHelper == nil {
260		tb.SkipNow()
261	}
262
263	dbStore := mainHelper.GetStore()
264	dbStore.DropAllTables()
265	dbStore.MarkSystemRanUnitTests()
266	searchEngine := mainHelper.GetSearchEngine()
267	th := setupTestHelper(dbStore, searchEngine, false, true, updateConfig, nil)
268	th.InitLogin()
269	return th
270}
271
272func SetupConfigWithStoreMock(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper {
273	th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, false, false, updateConfig, nil)
274	statusMock := mocks.StatusStore{}
275	statusMock.On("UpdateExpiredDNDStatuses").Return([]*model.Status{}, nil)
276	statusMock.On("Get", "user1").Return(&model.Status{UserId: "user1", Status: model.StatusOnline}, nil)
277	statusMock.On("UpdateLastActivityAt", "user1", mock.Anything).Return(nil)
278	statusMock.On("SaveOrUpdate", mock.AnythingOfType("*model.Status")).Return(nil)
279	emptyMockStore := mocks.Store{}
280	emptyMockStore.On("Close").Return(nil)
281	emptyMockStore.On("Status").Return(&statusMock)
282	th.App.Srv().Store = &emptyMockStore
283	return th
284}
285
286func SetupWithStoreMock(tb testing.TB) *TestHelper {
287	th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, false, false, nil, nil)
288	statusMock := mocks.StatusStore{}
289	statusMock.On("UpdateExpiredDNDStatuses").Return([]*model.Status{}, nil)
290	statusMock.On("Get", "user1").Return(&model.Status{UserId: "user1", Status: model.StatusOnline}, nil)
291	statusMock.On("UpdateLastActivityAt", "user1", mock.Anything).Return(nil)
292	statusMock.On("SaveOrUpdate", mock.AnythingOfType("*model.Status")).Return(nil)
293	emptyMockStore := mocks.Store{}
294	emptyMockStore.On("Close").Return(nil)
295	emptyMockStore.On("Status").Return(&statusMock)
296	th.App.Srv().Store = &emptyMockStore
297	return th
298}
299
300func SetupEnterpriseWithStoreMock(tb testing.TB) *TestHelper {
301	th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, true, false, nil, nil)
302	statusMock := mocks.StatusStore{}
303	statusMock.On("UpdateExpiredDNDStatuses").Return([]*model.Status{}, nil)
304	statusMock.On("Get", "user1").Return(&model.Status{UserId: "user1", Status: model.StatusOnline}, nil)
305	statusMock.On("UpdateLastActivityAt", "user1", mock.Anything).Return(nil)
306	statusMock.On("SaveOrUpdate", mock.AnythingOfType("*model.Status")).Return(nil)
307	emptyMockStore := mocks.Store{}
308	emptyMockStore.On("Close").Return(nil)
309	emptyMockStore.On("Status").Return(&statusMock)
310	th.App.Srv().Store = &emptyMockStore
311	return th
312}
313
314func SetupWithServerOptions(tb testing.TB, options []app.Option) *TestHelper {
315	if testing.Short() {
316		tb.SkipNow()
317	}
318
319	if mainHelper == nil {
320		tb.SkipNow()
321	}
322
323	dbStore := mainHelper.GetStore()
324	dbStore.DropAllTables()
325	dbStore.MarkSystemRanUnitTests()
326	mainHelper.PreloadMigrations()
327	searchEngine := mainHelper.GetSearchEngine()
328	th := setupTestHelper(dbStore, searchEngine, false, true, nil, options)
329	th.InitLogin()
330	return th
331}
332
333func (th *TestHelper) ShutdownApp() {
334	done := make(chan bool)
335	go func() {
336		th.Server.Shutdown()
337		close(done)
338	}()
339
340	select {
341	case <-done:
342	case <-time.After(30 * time.Second):
343		// panic instead of fatal to terminate all tests in this package, otherwise the
344		// still running App could spuriously fail subsequent tests.
345		panic("failed to shutdown App within 30 seconds")
346	}
347}
348
349func (th *TestHelper) TearDown() {
350	if th.IncludeCacheLayer {
351		// Clean all the caches
352		th.App.Srv().InvalidateAllCaches()
353	}
354	th.ShutdownApp()
355}
356
357var initBasicOnce sync.Once
358var userCache struct {
359	SystemAdminUser   *model.User
360	SystemManagerUser *model.User
361	TeamAdminUser     *model.User
362	BasicUser         *model.User
363	BasicUser2        *model.User
364}
365
366func (th *TestHelper) InitLogin() *TestHelper {
367	th.waitForConnectivity()
368
369	// create users once and cache them because password hashing is slow
370	initBasicOnce.Do(func() {
371		th.SystemAdminUser = th.CreateUser()
372		th.App.UpdateUserRoles(th.SystemAdminUser.Id, model.SystemUserRoleId+" "+model.SystemAdminRoleId, false)
373		th.SystemAdminUser, _ = th.App.GetUser(th.SystemAdminUser.Id)
374		userCache.SystemAdminUser = th.SystemAdminUser.DeepCopy()
375
376		th.SystemManagerUser = th.CreateUser()
377		th.App.UpdateUserRoles(th.SystemManagerUser.Id, model.SystemUserRoleId+" "+model.SystemManagerRoleId, false)
378		th.SystemManagerUser, _ = th.App.GetUser(th.SystemManagerUser.Id)
379		userCache.SystemManagerUser = th.SystemManagerUser.DeepCopy()
380
381		th.TeamAdminUser = th.CreateUser()
382		th.App.UpdateUserRoles(th.TeamAdminUser.Id, model.SystemUserRoleId, false)
383		th.TeamAdminUser, _ = th.App.GetUser(th.TeamAdminUser.Id)
384		userCache.TeamAdminUser = th.TeamAdminUser.DeepCopy()
385
386		th.BasicUser = th.CreateUser()
387		th.BasicUser, _ = th.App.GetUser(th.BasicUser.Id)
388		userCache.BasicUser = th.BasicUser.DeepCopy()
389
390		th.BasicUser2 = th.CreateUser()
391		th.BasicUser2, _ = th.App.GetUser(th.BasicUser2.Id)
392		userCache.BasicUser2 = th.BasicUser2.DeepCopy()
393	})
394	// restore cached users
395	th.SystemAdminUser = userCache.SystemAdminUser.DeepCopy()
396	th.SystemManagerUser = userCache.SystemManagerUser.DeepCopy()
397	th.TeamAdminUser = userCache.TeamAdminUser.DeepCopy()
398	th.BasicUser = userCache.BasicUser.DeepCopy()
399	th.BasicUser2 = userCache.BasicUser2.DeepCopy()
400	mainHelper.GetSQLStore().GetMaster().Insert(th.SystemAdminUser, th.TeamAdminUser, th.BasicUser, th.BasicUser2, th.SystemManagerUser)
401	// restore non hashed password for login
402	th.SystemAdminUser.Password = "Pa$$word11"
403	th.TeamAdminUser.Password = "Pa$$word11"
404	th.BasicUser.Password = "Pa$$word11"
405	th.BasicUser2.Password = "Pa$$word11"
406	th.SystemManagerUser.Password = "Pa$$word11"
407
408	var wg sync.WaitGroup
409	wg.Add(3)
410	go func() {
411		th.LoginSystemAdmin()
412		wg.Done()
413	}()
414	go func() {
415		th.LoginSystemManager()
416		wg.Done()
417	}()
418	go func() {
419		th.LoginTeamAdmin()
420		wg.Done()
421	}()
422	wg.Wait()
423	return th
424}
425
426func (th *TestHelper) InitBasic() *TestHelper {
427	th.BasicTeam = th.CreateTeam()
428	th.BasicChannel = th.CreatePublicChannel()
429	th.BasicPrivateChannel = th.CreatePrivateChannel()
430	th.BasicPrivateChannel2 = th.CreatePrivateChannel()
431	th.BasicDeletedChannel = th.CreatePublicChannel()
432	th.BasicChannel2 = th.CreatePublicChannel()
433	th.BasicPost = th.CreatePost()
434	th.LinkUserToTeam(th.BasicUser, th.BasicTeam)
435	th.LinkUserToTeam(th.BasicUser2, th.BasicTeam)
436	th.App.AddUserToChannel(th.BasicUser, th.BasicChannel, false)
437	th.App.AddUserToChannel(th.BasicUser2, th.BasicChannel, false)
438	th.App.AddUserToChannel(th.BasicUser, th.BasicChannel2, false)
439	th.App.AddUserToChannel(th.BasicUser2, th.BasicChannel2, false)
440	th.App.AddUserToChannel(th.BasicUser, th.BasicPrivateChannel, false)
441	th.App.AddUserToChannel(th.BasicUser2, th.BasicPrivateChannel, false)
442	th.App.AddUserToChannel(th.BasicUser, th.BasicDeletedChannel, false)
443	th.App.AddUserToChannel(th.BasicUser2, th.BasicDeletedChannel, false)
444	th.App.UpdateUserRoles(th.BasicUser.Id, model.SystemUserRoleId, false)
445	th.Client.DeleteChannel(th.BasicDeletedChannel.Id)
446	th.LoginBasic()
447	th.Group = th.CreateGroup()
448
449	return th
450}
451
452func (th *TestHelper) waitForConnectivity() {
453	for i := 0; i < 1000; i++ {
454		conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", th.App.Srv().ListenAddr.Port))
455		if err == nil {
456			conn.Close()
457			return
458		}
459		time.Sleep(time.Millisecond * 20)
460	}
461	panic("unable to connect")
462}
463
464func (th *TestHelper) CreateClient() *model.Client4 {
465	return model.NewAPIv4Client(fmt.Sprintf("http://localhost:%v", th.App.Srv().ListenAddr.Port))
466}
467
468// ToDo: maybe move this to NewAPIv4SocketClient and reuse it in mmctl
469func (th *TestHelper) CreateLocalClient(socketPath string) *model.Client4 {
470	httpClient := &http.Client{
471		Transport: &http.Transport{
472			Dial: func(network, addr string) (net.Conn, error) {
473				return net.Dial("unix", socketPath)
474			},
475		},
476	}
477
478	return &model.Client4{
479		APIURL:     "http://_" + model.APIURLSuffix,
480		HTTPClient: httpClient,
481	}
482}
483
484func (th *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, error) {
485	return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", th.App.Srv().ListenAddr.Port), th.Client.AuthToken)
486}
487
488func (th *TestHelper) CreateWebSocketSystemAdminClient() (*model.WebSocketClient, error) {
489	return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", th.App.Srv().ListenAddr.Port), th.SystemAdminClient.AuthToken)
490}
491
492func (th *TestHelper) CreateWebSocketSystemManagerClient() (*model.WebSocketClient, error) {
493	return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", th.App.Srv().ListenAddr.Port), th.SystemManagerClient.AuthToken)
494}
495
496func (th *TestHelper) CreateWebSocketClientWithClient(client *model.Client4) (*model.WebSocketClient, error) {
497	return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", th.App.Srv().ListenAddr.Port), client.AuthToken)
498}
499
500func (th *TestHelper) CreateBotWithSystemAdminClient() *model.Bot {
501	return th.CreateBotWithClient((th.SystemAdminClient))
502}
503
504func (th *TestHelper) CreateBotWithClient(client *model.Client4) *model.Bot {
505	bot := &model.Bot{
506		Username:    GenerateTestUsername(),
507		DisplayName: "a bot",
508		Description: "bot",
509	}
510
511	rbot, _, err := client.CreateBot(bot)
512	if err != nil {
513		panic(err)
514	}
515	return rbot
516}
517
518func (th *TestHelper) CreateUser() *model.User {
519	return th.CreateUserWithClient(th.Client)
520}
521
522func (th *TestHelper) CreateTeam() *model.Team {
523	return th.CreateTeamWithClient(th.Client)
524}
525
526func (th *TestHelper) CreateTeamWithClient(client *model.Client4) *model.Team {
527	id := model.NewId()
528	team := &model.Team{
529		DisplayName: "dn_" + id,
530		Name:        GenerateTestTeamName(),
531		Email:       th.GenerateTestEmail(),
532		Type:        model.TeamOpen,
533	}
534
535	rteam, _, err := client.CreateTeam(team)
536	if err != nil {
537		panic(err)
538	}
539	return rteam
540}
541
542func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
543	id := model.NewId()
544
545	user := &model.User{
546		Email:     th.GenerateTestEmail(),
547		Username:  GenerateTestUsername(),
548		Nickname:  "nn_" + id,
549		FirstName: "f_" + id,
550		LastName:  "l_" + id,
551		Password:  "Pa$$word11",
552	}
553
554	ruser, _, err := client.CreateUser(user)
555	if err != nil {
556		panic(err)
557	}
558
559	ruser.Password = "Pa$$word11"
560	_, err = th.App.Srv().Store.User().VerifyEmail(ruser.Id, ruser.Email)
561	if err != nil {
562		return nil
563	}
564	return ruser
565}
566
567func (th *TestHelper) CreateUserWithAuth(authService string) *model.User {
568	id := model.NewId()
569	user := &model.User{
570		Email:         "success+" + id + "@simulator.amazonses.com",
571		Username:      "un_" + id,
572		Nickname:      "nn_" + id,
573		EmailVerified: true,
574		AuthService:   authService,
575	}
576	user, err := th.App.CreateUser(th.Context, user)
577	if err != nil {
578		panic(err)
579	}
580	return user
581}
582
583func (th *TestHelper) SetupLdapConfig() {
584	th.App.UpdateConfig(func(cfg *model.Config) {
585		*cfg.ServiceSettings.EnableMultifactorAuthentication = true
586		*cfg.LdapSettings.Enable = true
587		*cfg.LdapSettings.EnableSync = true
588		*cfg.LdapSettings.LdapServer = "dockerhost"
589		*cfg.LdapSettings.BaseDN = "dc=mm,dc=test,dc=com"
590		*cfg.LdapSettings.BindUsername = "cn=admin,dc=mm,dc=test,dc=com"
591		*cfg.LdapSettings.BindPassword = "mostest"
592		*cfg.LdapSettings.FirstNameAttribute = "cn"
593		*cfg.LdapSettings.LastNameAttribute = "sn"
594		*cfg.LdapSettings.NicknameAttribute = "cn"
595		*cfg.LdapSettings.EmailAttribute = "mail"
596		*cfg.LdapSettings.UsernameAttribute = "uid"
597		*cfg.LdapSettings.IdAttribute = "cn"
598		*cfg.LdapSettings.LoginIdAttribute = "uid"
599		*cfg.LdapSettings.SkipCertificateVerification = true
600		*cfg.LdapSettings.GroupFilter = ""
601		*cfg.LdapSettings.GroupDisplayNameAttribute = "cN"
602		*cfg.LdapSettings.GroupIdAttribute = "entRyUuId"
603		*cfg.LdapSettings.MaxPageSize = 0
604	})
605	th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
606}
607
608func (th *TestHelper) SetupSamlConfig() {
609	th.App.UpdateConfig(func(cfg *model.Config) {
610		*cfg.SamlSettings.Enable = true
611		*cfg.SamlSettings.Verify = false
612		*cfg.SamlSettings.Encrypt = false
613		*cfg.SamlSettings.IdpURL = "https://does.notmatter.com"
614		*cfg.SamlSettings.IdpDescriptorURL = "https://localhost/adfs/services/trust"
615		*cfg.SamlSettings.AssertionConsumerServiceURL = "https://localhost/login/sso/saml"
616		*cfg.SamlSettings.ServiceProviderIdentifier = "https://localhost/login/sso/saml"
617		*cfg.SamlSettings.IdpCertificateFile = app.SamlIdpCertificateName
618		*cfg.SamlSettings.PrivateKeyFile = app.SamlPrivateKeyName
619		*cfg.SamlSettings.PublicCertificateFile = app.SamlPublicCertificateName
620		*cfg.SamlSettings.EmailAttribute = "Email"
621		*cfg.SamlSettings.UsernameAttribute = "Username"
622		*cfg.SamlSettings.FirstNameAttribute = "FirstName"
623		*cfg.SamlSettings.LastNameAttribute = "LastName"
624		*cfg.SamlSettings.NicknameAttribute = ""
625		*cfg.SamlSettings.PositionAttribute = ""
626		*cfg.SamlSettings.LocaleAttribute = ""
627		*cfg.SamlSettings.SignatureAlgorithm = model.SamlSettingsSignatureAlgorithmSha256
628		*cfg.SamlSettings.CanonicalAlgorithm = model.SamlSettingsCanonicalAlgorithmC14n11
629	})
630	th.App.Srv().SetLicense(model.NewTestLicense("saml"))
631}
632
633func (th *TestHelper) CreatePublicChannel() *model.Channel {
634	return th.CreateChannelWithClient(th.Client, model.ChannelTypeOpen)
635}
636
637func (th *TestHelper) CreatePrivateChannel() *model.Channel {
638	return th.CreateChannelWithClient(th.Client, model.ChannelTypePrivate)
639}
640
641func (th *TestHelper) CreateChannelWithClient(client *model.Client4, channelType model.ChannelType) *model.Channel {
642	return th.CreateChannelWithClientAndTeam(client, channelType, th.BasicTeam.Id)
643}
644
645func (th *TestHelper) CreateChannelWithClientAndTeam(client *model.Client4, channelType model.ChannelType, teamId string) *model.Channel {
646	id := model.NewId()
647
648	channel := &model.Channel{
649		DisplayName: "dn_" + id,
650		Name:        GenerateTestChannelName(),
651		Type:        channelType,
652		TeamId:      teamId,
653	}
654
655	rchannel, _, err := client.CreateChannel(channel)
656	if err != nil {
657		panic(err)
658	}
659	return rchannel
660}
661
662func (th *TestHelper) CreatePost() *model.Post {
663	return th.CreatePostWithClient(th.Client, th.BasicChannel)
664}
665
666func (th *TestHelper) CreatePinnedPost() *model.Post {
667	return th.CreatePinnedPostWithClient(th.Client, th.BasicChannel)
668}
669
670func (th *TestHelper) CreateMessagePost(message string) *model.Post {
671	return th.CreateMessagePostWithClient(th.Client, th.BasicChannel, message)
672}
673
674func (th *TestHelper) CreatePostWithClient(client *model.Client4, channel *model.Channel) *model.Post {
675	id := model.NewId()
676
677	post := &model.Post{
678		ChannelId: channel.Id,
679		Message:   "message_" + id,
680	}
681
682	rpost, _, err := client.CreatePost(post)
683	if err != nil {
684		panic(err)
685	}
686	return rpost
687}
688
689func (th *TestHelper) CreatePinnedPostWithClient(client *model.Client4, channel *model.Channel) *model.Post {
690	id := model.NewId()
691
692	post := &model.Post{
693		ChannelId: channel.Id,
694		Message:   "message_" + id,
695		IsPinned:  true,
696	}
697
698	rpost, _, err := client.CreatePost(post)
699	if err != nil {
700		panic(err)
701	}
702	return rpost
703}
704
705func (th *TestHelper) CreateMessagePostWithClient(client *model.Client4, channel *model.Channel, message string) *model.Post {
706	post := &model.Post{
707		ChannelId: channel.Id,
708		Message:   message,
709	}
710
711	rpost, _, err := client.CreatePost(post)
712	if err != nil {
713		panic(err)
714	}
715	return rpost
716}
717
718func (th *TestHelper) CreateMessagePostNoClient(channel *model.Channel, message string, createAtTime int64) *model.Post {
719	post, err := th.App.Srv().Store.Post().Save(&model.Post{
720		UserId:    th.BasicUser.Id,
721		ChannelId: channel.Id,
722		Message:   message,
723		CreateAt:  createAtTime,
724	})
725
726	if err != nil {
727		panic(err)
728	}
729
730	return post
731}
732
733func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
734	var err *model.AppError
735	var channel *model.Channel
736	if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
737		panic(err)
738	}
739	return channel
740}
741
742func (th *TestHelper) LoginBasic() {
743	th.LoginBasicWithClient(th.Client)
744}
745
746func (th *TestHelper) LoginBasic2() {
747	th.LoginBasic2WithClient(th.Client)
748}
749
750func (th *TestHelper) LoginTeamAdmin() {
751	th.LoginTeamAdminWithClient(th.Client)
752}
753
754func (th *TestHelper) LoginSystemAdmin() {
755	th.LoginSystemAdminWithClient(th.SystemAdminClient)
756}
757
758func (th *TestHelper) LoginSystemManager() {
759	th.LoginSystemManagerWithClient(th.SystemManagerClient)
760}
761
762func (th *TestHelper) LoginBasicWithClient(client *model.Client4) {
763	_, _, err := client.Login(th.BasicUser.Email, th.BasicUser.Password)
764	if err != nil {
765		panic(err)
766	}
767}
768
769func (th *TestHelper) LoginBasic2WithClient(client *model.Client4) {
770	_, _, err := client.Login(th.BasicUser2.Email, th.BasicUser2.Password)
771	if err != nil {
772		panic(err)
773	}
774}
775
776func (th *TestHelper) LoginTeamAdminWithClient(client *model.Client4) {
777	_, _, err := client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
778	if err != nil {
779		panic(err)
780	}
781}
782
783func (th *TestHelper) LoginSystemManagerWithClient(client *model.Client4) {
784	_, _, err := client.Login(th.SystemManagerUser.Email, th.SystemManagerUser.Password)
785	if err != nil {
786		panic(err)
787	}
788}
789
790func (th *TestHelper) LoginSystemAdminWithClient(client *model.Client4) {
791	_, _, err := client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password)
792	if err != nil {
793		panic(err)
794	}
795}
796
797func (th *TestHelper) UpdateActiveUser(user *model.User, active bool) {
798	_, err := th.App.UpdateActive(th.Context, user, active)
799	if err != nil {
800		panic(err)
801	}
802}
803
804func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
805	_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
806	if err != nil {
807		panic(err)
808	}
809}
810
811func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
812	member, err := th.App.AddUserToChannel(user, channel, false)
813	if err != nil {
814		panic(err)
815	}
816	return member
817}
818
819func (th *TestHelper) GenerateTestEmail() string {
820	if *th.App.Config().EmailSettings.SMTPServer != "localhost" && os.Getenv("CI_INBUCKET_PORT") == "" {
821		return strings.ToLower("success+" + model.NewId() + "@simulator.amazonses.com")
822	}
823	return strings.ToLower(model.NewId() + "@localhost")
824}
825
826func (th *TestHelper) CreateGroup() *model.Group {
827	id := model.NewId()
828	group := &model.Group{
829		Name:        model.NewString("n-" + id),
830		DisplayName: "dn_" + id,
831		Source:      model.GroupSourceLdap,
832		RemoteId:    "ri_" + id,
833	}
834
835	group, err := th.App.CreateGroup(group)
836	if err != nil {
837		panic(err)
838	}
839	return group
840}
841
842// TestForSystemAdminAndLocal runs a test function for both
843// SystemAdmin and Local clients. Several endpoints work in the same
844// way when used by a fully privileged user and through the local
845// mode, so this helper facilitates checking both
846func (th *TestHelper) TestForSystemAdminAndLocal(t *testing.T, f func(*testing.T, *model.Client4), name ...string) {
847	var testName string
848	if len(name) > 0 {
849		testName = name[0] + "/"
850	}
851
852	t.Run(testName+"SystemAdminClient", func(t *testing.T) {
853		f(t, th.SystemAdminClient)
854	})
855
856	t.Run(testName+"LocalClient", func(t *testing.T) {
857		f(t, th.LocalClient)
858	})
859}
860
861// TestForAllClients runs a test function for all the clients
862// registered in the TestHelper
863func (th *TestHelper) TestForAllClients(t *testing.T, f func(*testing.T, *model.Client4), name ...string) {
864	var testName string
865	if len(name) > 0 {
866		testName = name[0] + "/"
867	}
868
869	t.Run(testName+"Client", func(t *testing.T) {
870		f(t, th.Client)
871	})
872
873	t.Run(testName+"SystemAdminClient", func(t *testing.T) {
874		f(t, th.SystemAdminClient)
875	})
876
877	t.Run(testName+"LocalClient", func(t *testing.T) {
878		f(t, th.LocalClient)
879	})
880}
881
882func GenerateTestUsername() string {
883	return "fakeuser" + model.NewRandomString(10)
884}
885
886func GenerateTestTeamName() string {
887	return "faketeam" + model.NewRandomString(6)
888}
889
890func GenerateTestChannelName() string {
891	return "fakechannel" + model.NewRandomString(10)
892}
893
894func GenerateTestAppName() string {
895	return "fakeoauthapp" + model.NewRandomString(10)
896}
897
898func GenerateTestId() string {
899	return model.NewId()
900}
901
902func CheckUserSanitization(t *testing.T, user *model.User) {
903	t.Helper()
904
905	require.Equal(t, "", user.Password, "password wasn't blank")
906	require.Empty(t, user.AuthData, "auth data wasn't blank")
907	require.Equal(t, "", user.MfaSecret, "mfa secret wasn't blank")
908}
909
910func CheckEtag(t *testing.T, data interface{}, resp *model.Response) {
911	t.Helper()
912
913	require.Empty(t, data)
914	require.Equal(t, http.StatusNotModified, resp.StatusCode, "wrong status code for etag")
915}
916
917func checkHTTPStatus(t *testing.T, resp *model.Response, expectedStatus int) {
918	t.Helper()
919
920	require.NotNilf(t, resp, "Unexpected nil response, expected http status:%v", expectedStatus)
921	require.Equalf(t, expectedStatus, resp.StatusCode, "Expected http status:%v, got %v", expectedStatus, resp.StatusCode)
922}
923
924func CheckOKStatus(t *testing.T, resp *model.Response) {
925	t.Helper()
926	checkHTTPStatus(t, resp, http.StatusOK)
927}
928
929func CheckCreatedStatus(t *testing.T, resp *model.Response) {
930	t.Helper()
931	checkHTTPStatus(t, resp, http.StatusCreated)
932}
933
934func CheckForbiddenStatus(t *testing.T, resp *model.Response) {
935	t.Helper()
936	checkHTTPStatus(t, resp, http.StatusForbidden)
937}
938
939func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) {
940	t.Helper()
941	checkHTTPStatus(t, resp, http.StatusUnauthorized)
942}
943
944func CheckNotFoundStatus(t *testing.T, resp *model.Response) {
945	t.Helper()
946	checkHTTPStatus(t, resp, http.StatusNotFound)
947}
948
949func CheckBadRequestStatus(t *testing.T, resp *model.Response) {
950	t.Helper()
951	checkHTTPStatus(t, resp, http.StatusBadRequest)
952}
953
954func CheckNotImplementedStatus(t *testing.T, resp *model.Response) {
955	t.Helper()
956	checkHTTPStatus(t, resp, http.StatusNotImplemented)
957}
958
959func CheckRequestEntityTooLargeStatus(t *testing.T, resp *model.Response) {
960	t.Helper()
961	checkHTTPStatus(t, resp, http.StatusRequestEntityTooLarge)
962}
963
964func CheckInternalErrorStatus(t *testing.T, resp *model.Response) {
965	t.Helper()
966	checkHTTPStatus(t, resp, http.StatusInternalServerError)
967}
968
969func CheckServiceUnavailableStatus(t *testing.T, resp *model.Response) {
970	t.Helper()
971	checkHTTPStatus(t, resp, http.StatusServiceUnavailable)
972}
973
974func CheckErrorID(t *testing.T, err error, errorId string) {
975	t.Helper()
976
977	require.Error(t, err, "should have errored with id: %s", errorId)
978
979	var appError *model.AppError
980	ok := errors.As(err, &appError)
981	require.True(t, ok, "should have been a model.AppError")
982
983	require.Equalf(t, errorId, appError.Id, "incorrect error id, actual: %s, expected: %s", appError.Id, errorId)
984}
985
986func CheckErrorMessage(t *testing.T, err error, message string) {
987	t.Helper()
988
989	require.Error(t, err, "should have errored with message: %s", message)
990
991	var appError *model.AppError
992	ok := errors.As(err, &appError)
993	require.True(t, ok, "should have been a model.AppError")
994
995	require.Equalf(t, message, appError.Message, "incorrect error message, actual: %s, expected: %s", appError.Id, message)
996}
997
998func CheckStartsWith(t *testing.T, value, prefix, message string) {
999	t.Helper()
1000
1001	require.True(t, strings.HasPrefix(value, prefix), message, value)
1002}
1003
1004// Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
1005// If signV2 input is false, function always returns signature v4.
1006//
1007// Additionally this function also takes a user defined region, if set
1008// disables automatic region lookup.
1009func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool, region string) (*s3.Client, error) {
1010	var creds *credentials.Credentials
1011	if signV2 {
1012		creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV2)
1013	} else {
1014		creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV4)
1015	}
1016
1017	opts := s3.Options{
1018		Creds:  creds,
1019		Secure: secure,
1020		Region: region,
1021	}
1022	return s3.New(endpoint, &opts)
1023}
1024
1025func (th *TestHelper) cleanupTestFile(info *model.FileInfo) error {
1026	cfg := th.App.Config()
1027	if *cfg.FileSettings.DriverName == model.ImageDriverS3 {
1028		endpoint := *cfg.FileSettings.AmazonS3Endpoint
1029		accessKey := *cfg.FileSettings.AmazonS3AccessKeyId
1030		secretKey := *cfg.FileSettings.AmazonS3SecretAccessKey
1031		secure := *cfg.FileSettings.AmazonS3SSL
1032		signV2 := *cfg.FileSettings.AmazonS3SignV2
1033		region := *cfg.FileSettings.AmazonS3Region
1034		s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region)
1035		if err != nil {
1036			return err
1037		}
1038		bucket := *cfg.FileSettings.AmazonS3Bucket
1039		if err := s3Clnt.RemoveObject(context.Background(), bucket, info.Path, s3.RemoveObjectOptions{}); err != nil {
1040			return err
1041		}
1042
1043		if info.ThumbnailPath != "" {
1044			if err := s3Clnt.RemoveObject(context.Background(), bucket, info.ThumbnailPath, s3.RemoveObjectOptions{}); err != nil {
1045				return err
1046			}
1047		}
1048
1049		if info.PreviewPath != "" {
1050			if err := s3Clnt.RemoveObject(context.Background(), bucket, info.PreviewPath, s3.RemoveObjectOptions{}); err != nil {
1051				return err
1052			}
1053		}
1054	} else if *cfg.FileSettings.DriverName == model.ImageDriverLocal {
1055		if err := os.Remove(*cfg.FileSettings.Directory + info.Path); err != nil {
1056			return err
1057		}
1058
1059		if info.ThumbnailPath != "" {
1060			if err := os.Remove(*cfg.FileSettings.Directory + info.ThumbnailPath); err != nil {
1061				return err
1062			}
1063		}
1064
1065		if info.PreviewPath != "" {
1066			if err := os.Remove(*cfg.FileSettings.Directory + info.PreviewPath); err != nil {
1067				return err
1068			}
1069		}
1070	}
1071
1072	return nil
1073}
1074
1075func (th *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Channel) {
1076	if cm, err := th.App.Srv().Store.Channel().GetMember(context.Background(), channel.Id, user.Id); err == nil {
1077		cm.SchemeAdmin = true
1078		if _, err = th.App.Srv().Store.Channel().UpdateMember(cm); err != nil {
1079			panic(err)
1080		}
1081	} else {
1082		panic(err)
1083	}
1084}
1085
1086func (th *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
1087	if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil {
1088		tm.SchemeAdmin = true
1089		if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
1090			panic(err)
1091		}
1092	} else {
1093		panic(err)
1094	}
1095}
1096
1097func (th *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
1098	if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil {
1099		tm.SchemeAdmin = false
1100		if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
1101			panic(err)
1102		}
1103	} else {
1104		panic(err)
1105	}
1106}
1107
1108func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
1109	results := make(map[string][]string)
1110
1111	for _, roleName := range []string{
1112		"system_user",
1113		"system_admin",
1114		"team_user",
1115		"team_admin",
1116		"channel_user",
1117		"channel_admin",
1118	} {
1119		role, err1 := th.App.GetRoleByName(context.Background(), roleName)
1120		if err1 != nil {
1121			panic(err1)
1122		}
1123
1124		results[roleName] = role.Permissions
1125	}
1126	return results
1127}
1128
1129func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
1130	for roleName, permissions := range data {
1131		role, err1 := th.App.GetRoleByName(context.Background(), roleName)
1132		if err1 != nil {
1133			panic(err1)
1134		}
1135
1136		if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") {
1137			continue
1138		}
1139
1140		role.Permissions = permissions
1141
1142		_, err2 := th.App.UpdateRole(role)
1143		if err2 != nil {
1144			panic(err2)
1145		}
1146	}
1147}
1148
1149func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
1150	role, err1 := th.App.GetRoleByName(context.Background(), roleName)
1151	if err1 != nil {
1152		panic(err1)
1153	}
1154
1155	var newPermissions []string
1156	for _, p := range role.Permissions {
1157		if p != permission {
1158			newPermissions = append(newPermissions, p)
1159		}
1160	}
1161
1162	if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
1163		return
1164	}
1165
1166	role.Permissions = newPermissions
1167
1168	_, err2 := th.App.UpdateRole(role)
1169	if err2 != nil {
1170		panic(err2)
1171	}
1172}
1173
1174func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
1175	role, err1 := th.App.GetRoleByName(context.Background(), roleName)
1176	if err1 != nil {
1177		panic(err1)
1178	}
1179
1180	for _, existingPermission := range role.Permissions {
1181		if existingPermission == permission {
1182			return
1183		}
1184	}
1185
1186	role.Permissions = append(role.Permissions, permission)
1187
1188	_, err2 := th.App.UpdateRole(role)
1189	if err2 != nil {
1190		panic(err2)
1191	}
1192}
1193
1194func (th *TestHelper) SetupTeamScheme() *model.Scheme {
1195	return th.SetupScheme(model.SchemeScopeTeam)
1196}
1197
1198func (th *TestHelper) SetupChannelScheme() *model.Scheme {
1199	return th.SetupScheme(model.SchemeScopeChannel)
1200}
1201
1202func (th *TestHelper) SetupScheme(scope string) *model.Scheme {
1203	scheme, err := th.App.CreateScheme(&model.Scheme{
1204		Name:        model.NewId(),
1205		DisplayName: model.NewId(),
1206		Scope:       scope,
1207	})
1208	if err != nil {
1209		panic(err)
1210	}
1211	return scheme
1212}
1213