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