github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/api/file_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "os" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/mattermost/mattermost-server/app" 18 "github.com/mattermost/mattermost-server/model" 19 "github.com/mattermost/mattermost-server/store" 20 "github.com/mattermost/mattermost-server/utils" 21 22 s3 "github.com/minio/minio-go" 23 "github.com/minio/minio-go/pkg/credentials" 24 ) 25 26 func TestUploadFile(t *testing.T) { 27 th := Setup().InitBasic().InitSystemAdmin() 28 defer th.TearDown() 29 30 if *th.App.Config().FileSettings.DriverName == "" { 31 t.Logf("skipping because no file driver is enabled") 32 return 33 } 34 35 Client := th.BasicClient 36 team := th.BasicTeam 37 user := th.BasicUser 38 channel := th.BasicChannel 39 40 var uploadInfo *model.FileInfo 41 var data []byte 42 var err error 43 if data, err = readTestFile("test.png"); err != nil { 44 t.Fatal(err) 45 } else if resp, err := Client.UploadPostAttachment(data, channel.Id, "test.png"); err != nil { 46 t.Fatal(err) 47 } else if len(resp.FileInfos) != 1 { 48 t.Fatal("should've returned a single file infos") 49 } else { 50 uploadInfo = resp.FileInfos[0] 51 } 52 53 // The returned file info from the upload call will be missing some fields that will be stored in the database 54 if uploadInfo.CreatorId != user.Id { 55 t.Fatal("file should be assigned to user") 56 } else if uploadInfo.PostId != "" { 57 t.Fatal("file shouldn't have a post") 58 } else if uploadInfo.Path != "" { 59 t.Fatal("file path should not be set on returned info") 60 } else if uploadInfo.ThumbnailPath != "" { 61 t.Fatal("file thumbnail path should not be set on returned info") 62 } else if uploadInfo.PreviewPath != "" { 63 t.Fatal("file preview path should not be set on returned info") 64 } 65 66 var info *model.FileInfo 67 if result := <-th.App.Srv.Store.FileInfo().Get(uploadInfo.Id); result.Err != nil { 68 t.Fatal(result.Err) 69 } else { 70 info = result.Data.(*model.FileInfo) 71 } 72 73 if info.Id != uploadInfo.Id { 74 t.Fatal("file id from response should match one stored in database") 75 } else if info.CreatorId != user.Id { 76 t.Fatal("file should be assigned to user") 77 } else if info.PostId != "" { 78 t.Fatal("file shouldn't have a post") 79 } else if info.Path == "" { 80 t.Fatal("file path should be set in database") 81 } else if info.ThumbnailPath == "" { 82 t.Fatal("file thumbnail path should be set in database") 83 } else if info.PreviewPath == "" { 84 t.Fatal("file preview path should be set in database") 85 } 86 87 date := time.Now().Format("20060102") 88 89 // This also makes sure that the relative path provided above is sanitized out 90 expectedPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test.png", date, team.Id, channel.Id, user.Id, info.Id) 91 if info.Path != expectedPath { 92 t.Logf("file is saved in %v", info.Path) 93 t.Fatalf("file should've been saved in %v", expectedPath) 94 } 95 96 expectedThumbnailPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_thumb.jpg", date, team.Id, channel.Id, user.Id, info.Id) 97 if info.ThumbnailPath != expectedThumbnailPath { 98 t.Logf("file thumbnail is saved in %v", info.ThumbnailPath) 99 t.Fatalf("file thumbnail should've been saved in %v", expectedThumbnailPath) 100 } 101 102 expectedPreviewPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_preview.jpg", date, team.Id, channel.Id, user.Id, info.Id) 103 if info.PreviewPath != expectedPreviewPath { 104 t.Logf("file preview is saved in %v", info.PreviewPath) 105 t.Fatalf("file preview should've been saved in %v", expectedPreviewPath) 106 } 107 108 if _, err := Client.UploadPostAttachment(data, model.NewId(), "test.png"); err == nil || err.StatusCode != http.StatusForbidden { 109 t.Fatal("should have failed - bad channel id") 110 } 111 112 if _, err := Client.UploadPostAttachment(data, "../../junk", "test.png"); err == nil || err.StatusCode != http.StatusForbidden { 113 t.Fatal("should have failed - bad channel id") 114 } 115 116 if _, err := th.SystemAdminClient.UploadPostAttachment(data, model.NewId(), "test.png"); err == nil || err.StatusCode != http.StatusForbidden { 117 t.Fatal("should have failed - bad channel id") 118 } 119 120 if _, err := th.SystemAdminClient.UploadPostAttachment(data, "../../junk", "test.png"); err == nil || err.StatusCode != http.StatusForbidden { 121 t.Fatal("should have failed - bad channel id") 122 } 123 124 enableFileAttachments := *th.App.Config().FileSettings.EnableFileAttachments 125 defer func() { 126 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = enableFileAttachments }) 127 }() 128 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = false }) 129 130 if data, err := readTestFile("test.png"); err != nil { 131 t.Fatal(err) 132 } else if _, err = Client.UploadPostAttachment(data, channel.Id, "test.png"); err == nil { 133 t.Fatal("should have errored") 134 } 135 136 // Wait a bit for files to ready 137 time.Sleep(2 * time.Second) 138 139 if err := cleanupTestFile(info, &th.App.Config().FileSettings); err != nil { 140 t.Fatal(err) 141 } 142 } 143 144 func TestGetFileInfo(t *testing.T) { 145 th := Setup().InitBasic() 146 defer th.TearDown() 147 148 if *th.App.Config().FileSettings.DriverName == "" { 149 t.Skip("skipping because no file driver is enabled") 150 } 151 152 Client := th.BasicClient 153 user := th.BasicUser 154 channel := th.BasicChannel 155 156 var fileId string 157 if data, err := readTestFile("test.png"); err != nil { 158 t.Fatal(err) 159 } else { 160 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 161 } 162 163 info, err := Client.GetFileInfo(fileId) 164 if err != nil { 165 t.Fatal(err) 166 } else if info.Id != fileId { 167 t.Fatal("got incorrect file") 168 } else if info.CreatorId != user.Id { 169 t.Fatal("file should be assigned to user") 170 } else if info.PostId != "" { 171 t.Fatal("file shouldn't have a post") 172 } else if info.Path != "" { 173 t.Fatal("file path shouldn't have been returned to client") 174 } else if info.ThumbnailPath != "" { 175 t.Fatal("file thumbnail path shouldn't have been returned to client") 176 } else if info.PreviewPath != "" { 177 t.Fatal("file preview path shouldn't have been returned to client") 178 } else if info.MimeType != "image/png" { 179 t.Fatal("mime type should've been image/png") 180 } 181 182 // Wait a bit for files to ready 183 time.Sleep(2 * time.Second) 184 185 // Other user shouldn't be able to get file info for this file before it's attached to a post 186 th.LoginBasic2() 187 188 if _, err := Client.GetFileInfo(fileId); err == nil { 189 t.Fatal("other user shouldn't be able to get file info before it's attached to a post") 190 } 191 192 // Hacky way to assign file to a post (usually would be done by CreatePost call) 193 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 194 195 // Other user shouldn't be able to get file info for this file if they're not in the channel for it 196 if _, err := Client.GetFileInfo(fileId); err == nil { 197 t.Fatal("other user shouldn't be able to get file info when not in channel") 198 } 199 200 Client.Must(Client.JoinChannel(channel.Id)) 201 202 // Other user should now be able to get file info 203 if info2, err := Client.GetFileInfo(fileId); err != nil { 204 t.Fatal(err) 205 } else if info2.Id != fileId { 206 t.Fatal("other user got incorrect file") 207 } 208 209 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 210 t.Fatal(err) 211 } 212 } 213 214 func TestGetFile(t *testing.T) { 215 th := Setup().InitBasic() 216 defer th.TearDown() 217 218 if *th.App.Config().FileSettings.DriverName == "" { 219 t.Skip("skipping because no file driver is enabled") 220 } 221 222 Client := th.BasicClient 223 channel := th.BasicChannel 224 225 var fileId string 226 data, err := readTestFile("test.png") 227 if err != nil { 228 t.Fatal(err) 229 } else { 230 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 231 } 232 233 // Wait a bit for files to ready 234 time.Sleep(2 * time.Second) 235 236 if body, err := Client.GetFile(fileId); err != nil { 237 t.Fatal(err) 238 } else { 239 received, err := ioutil.ReadAll(body) 240 if err != nil { 241 t.Fatal(err) 242 } else if len(received) != len(data) { 243 t.Fatal("received file should be the same size as the sent one") 244 } 245 246 for i := range data { 247 if data[i] != received[i] { 248 t.Fatal("received file didn't match sent one") 249 } 250 } 251 252 body.Close() 253 } 254 255 // Other user shouldn't be able to get file for this file before it's attached to a post 256 th.LoginBasic2() 257 258 if _, err := Client.GetFile(fileId); err == nil { 259 t.Fatal("other user shouldn't be able to get file before it's attached to a post") 260 } 261 262 // Hacky way to assign file to a post (usually would be done by CreatePost call) 263 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 264 265 // Other user shouldn't be able to get file for this file if they're not in the channel for it 266 if _, err := Client.GetFile(fileId); err == nil { 267 t.Fatal("other user shouldn't be able to get file when not in channel") 268 } 269 270 Client.Must(Client.JoinChannel(channel.Id)) 271 272 // Other user should now be able to get file 273 if body, err := Client.GetFile(fileId); err != nil { 274 t.Fatal(err) 275 } else { 276 received, err := ioutil.ReadAll(body) 277 if err != nil { 278 t.Fatal(err) 279 } else if len(received) != len(data) { 280 t.Fatal("received file should be the same size as the sent one") 281 } 282 283 for i := range data { 284 if data[i] != received[i] { 285 t.Fatal("received file didn't match sent one") 286 } 287 } 288 289 body.Close() 290 } 291 292 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 293 t.Fatal(err) 294 } 295 } 296 297 func TestGetFileThumbnail(t *testing.T) { 298 th := Setup().InitBasic() 299 defer th.TearDown() 300 301 if *th.App.Config().FileSettings.DriverName == "" { 302 t.Skip("skipping because no file driver is enabled") 303 } 304 305 Client := th.BasicClient 306 channel := th.BasicChannel 307 308 var fileId string 309 data, err := readTestFile("test.png") 310 if err != nil { 311 t.Fatal(err) 312 } else { 313 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 314 } 315 316 // Wait a bit for files to ready 317 time.Sleep(2 * time.Second) 318 319 if body, err := Client.GetFileThumbnail(fileId); err != nil { 320 t.Fatal(err) 321 } else { 322 body.Close() 323 } 324 325 // Other user shouldn't be able to get thumbnail for this file before it's attached to a post 326 th.LoginBasic2() 327 328 if _, err := Client.GetFileThumbnail(fileId); err == nil { 329 t.Fatal("other user shouldn't be able to get file before it's attached to a post") 330 } 331 332 // Hacky way to assign file to a post (usually would be done by CreatePost call) 333 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 334 335 // Other user shouldn't be able to get thumbnail for this file if they're not in the channel for it 336 if _, err := Client.GetFileThumbnail(fileId); err == nil { 337 t.Fatal("other user shouldn't be able to get file when not in channel") 338 } 339 340 Client.Must(Client.JoinChannel(channel.Id)) 341 342 // Other user should now be able to get thumbnail 343 if body, err := Client.GetFileThumbnail(fileId); err != nil { 344 t.Fatal(err) 345 } else { 346 body.Close() 347 } 348 349 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 350 t.Fatal(err) 351 } 352 } 353 354 func TestGetFilePreview(t *testing.T) { 355 th := Setup().InitBasic() 356 defer th.TearDown() 357 358 if *th.App.Config().FileSettings.DriverName == "" { 359 t.Skip("skipping because no file driver is enabled") 360 } 361 362 Client := th.BasicClient 363 channel := th.BasicChannel 364 365 var fileId string 366 data, err := readTestFile("test.png") 367 if err != nil { 368 t.Fatal(err) 369 } else { 370 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 371 } 372 373 // Wait a bit for files to ready 374 time.Sleep(2 * time.Second) 375 376 if body, err := Client.GetFilePreview(fileId); err != nil { 377 t.Fatal(err) 378 } else { 379 body.Close() 380 } 381 382 // Other user shouldn't be able to get preview for this file before it's attached to a post 383 th.LoginBasic2() 384 385 if _, err := Client.GetFilePreview(fileId); err == nil { 386 t.Fatal("other user shouldn't be able to get file before it's attached to a post") 387 } 388 389 // Hacky way to assign file to a post (usually would be done by CreatePost call) 390 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 391 392 // Other user shouldn't be able to get preview for this file if they're not in the channel for it 393 if _, err := Client.GetFilePreview(fileId); err == nil { 394 t.Fatal("other user shouldn't be able to get file when not in channel") 395 } 396 397 Client.Must(Client.JoinChannel(channel.Id)) 398 399 // Other user should now be able to get preview 400 if body, err := Client.GetFilePreview(fileId); err != nil { 401 t.Fatal(err) 402 } else { 403 body.Close() 404 } 405 406 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 407 t.Fatal(err) 408 } 409 } 410 411 func TestGetPublicFile(t *testing.T) { 412 th := Setup().InitBasic() 413 defer th.TearDown() 414 415 if *th.App.Config().FileSettings.DriverName == "" { 416 t.Skip("skipping because no file driver is enabled") 417 } 418 419 enablePublicLink := th.App.Config().FileSettings.EnablePublicLink 420 publicLinkSalt := *th.App.Config().FileSettings.PublicLinkSalt 421 defer func() { 422 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink }) 423 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = publicLinkSalt }) 424 }() 425 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 426 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() }) 427 428 Client := th.BasicClient 429 channel := th.BasicChannel 430 431 var fileId string 432 data, err := readTestFile("test.png") 433 if err != nil { 434 t.Fatal(err) 435 } else { 436 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 437 } 438 439 // Hacky way to assign file to a post (usually would be done by CreatePost call) 440 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 441 442 link := Client.MustGeneric(Client.GetPublicLink(fileId)).(string) 443 444 // Wait a bit for files to ready 445 time.Sleep(2 * time.Second) 446 447 if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK { 448 t.Log(link) 449 t.Fatal("failed to get image with public link", err) 450 } 451 452 if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest { 453 t.Fatal("should've failed to get image with public link without hash", resp.Status) 454 } 455 456 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false }) 457 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented { 458 t.Fatal("should've failed to get image with disabled public link") 459 } 460 461 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 462 463 // test after the salt has changed 464 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() }) 465 466 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest { 467 t.Fatal("should've failed to get image with public link after salt changed") 468 } 469 470 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest { 471 t.Fatal("should've failed to get image with public link after salt changed") 472 } 473 474 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 475 t.Fatal(err) 476 } 477 } 478 479 func TestGetPublicFileOld(t *testing.T) { 480 th := Setup().InitBasic() 481 defer th.TearDown() 482 483 if *th.App.Config().FileSettings.DriverName == "" { 484 t.Skip("skipping because no file driver is enabled") 485 } 486 487 enablePublicLink := th.App.Config().FileSettings.EnablePublicLink 488 publicLinkSalt := *th.App.Config().FileSettings.PublicLinkSalt 489 defer func() { 490 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink }) 491 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = publicLinkSalt }) 492 }() 493 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 494 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() }) 495 496 channel := th.BasicChannel 497 498 var fileId string 499 data, err := readTestFile("test.png") 500 if err != nil { 501 t.Fatal(err) 502 } else { 503 //fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 504 fileId = model.NewId() 505 fileInfo := model.FileInfo{ 506 Id: fileId, 507 CreateAt: model.GetMillis(), 508 CreatorId: th.BasicUser.Id, 509 Path: fmt.Sprintf("teams/%s/channels/%s/users/%s/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId, "test.png"), 510 } 511 store.Must(th.App.Srv.Store.FileInfo().Save(&fileInfo)) 512 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId), "test.png") 513 } 514 515 // Hacky way to assign file to a post (usually would be done by CreatePost call) 516 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 517 518 // reconstruct old style of link 519 siteURL := fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) 520 link := generatePublicLinkOld(siteURL, th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId+"/test.png", *th.App.Config().FileSettings.PublicLinkSalt) 521 522 // Wait a bit for files to ready 523 time.Sleep(2 * time.Second) 524 525 if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK { 526 t.Fatalf("failed to get image with public link err=%v resp=%v", err, resp) 527 } 528 529 if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest { 530 t.Fatal("should've failed to get image with public link without hash", resp.Status) 531 } 532 533 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false }) 534 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented { 535 t.Fatal("should've failed to get image with disabled public link") 536 } 537 538 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 539 540 // test after the salt has changed 541 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() }) 542 543 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest { 544 t.Fatal("should've failed to get image with public link after salt changed") 545 } 546 547 if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest { 548 t.Fatal("should've failed to get image with public link after salt changed") 549 } 550 551 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 552 t.Fatal(err) 553 } 554 } 555 556 func generatePublicLinkOld(siteURL, teamId, channelId, userId, filename, salt string) string { 557 hash := app.GeneratePublicLinkHash(filename, salt) 558 return fmt.Sprintf("%s%s/public/files/get/%s/%s/%s/%s?h=%s", siteURL, model.API_URL_SUFFIX_V3, teamId, channelId, userId, filename, hash) 559 } 560 561 func TestGetPublicLink(t *testing.T) { 562 th := Setup().InitBasic() 563 defer th.TearDown() 564 565 if *th.App.Config().FileSettings.DriverName == "" { 566 t.Skip("skipping because no file driver is enabled") 567 } 568 569 enablePublicLink := th.App.Config().FileSettings.EnablePublicLink 570 defer func() { 571 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink }) 572 }() 573 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 574 575 Client := th.BasicClient 576 channel := th.BasicChannel 577 578 var fileId string 579 data, err := readTestFile("test.png") 580 if err != nil { 581 t.Fatal(err) 582 } else { 583 fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 584 } 585 586 if _, err := Client.GetPublicLink(fileId); err == nil { 587 t.Fatal("should've failed to get public link before file is attached to a post") 588 } 589 590 // Hacky way to assign file to a post (usually would be done by CreatePost call) 591 store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) 592 593 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false }) 594 595 if _, err := Client.GetPublicLink(fileId); err == nil { 596 t.Fatal("should've failed to get public link when disabled") 597 } 598 599 th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true }) 600 601 if link, err := Client.GetPublicLink(fileId); err != nil { 602 t.Fatal(err) 603 } else if link == "" { 604 t.Fatal("should've received public link") 605 } 606 607 // Other user shouldn't be able to get public link for this file if they're not in the channel for it 608 th.LoginBasic2() 609 610 if _, err := Client.GetPublicLink(fileId); err == nil { 611 t.Fatal("other user shouldn't be able to get file when not in channel") 612 } 613 614 Client.Must(Client.JoinChannel(channel.Id)) 615 616 // Other user should now be able to get public link 617 if link, err := Client.GetPublicLink(fileId); err != nil { 618 t.Fatal(err) 619 } else if link == "" { 620 t.Fatal("should've received public link") 621 } 622 623 // Wait a bit for files to ready 624 time.Sleep(2 * time.Second) 625 626 if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil { 627 t.Fatal(err) 628 } 629 } 630 631 func TestMigrateFilenamesToFileInfos(t *testing.T) { 632 th := Setup().InitBasic() 633 defer th.TearDown() 634 635 if *th.App.Config().FileSettings.DriverName == "" { 636 t.Skip("skipping because no file driver is enabled") 637 } 638 639 Client := th.BasicClient 640 641 user1 := th.BasicUser 642 643 channel1 := Client.Must(Client.CreateChannel(&model.Channel{ 644 Name: model.NewId(), 645 Type: model.CHANNEL_OPEN, 646 // No TeamId set to simulate a direct channel 647 })).Data.(*model.Channel) 648 649 var fileId1 string 650 var fileId2 string 651 data, err := readTestFile("test.png") 652 if err != nil { 653 t.Fatal(err) 654 } else { 655 fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 656 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId1), "test.png") 657 fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 658 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId2), "test.png") 659 } 660 661 // Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post 662 post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{ 663 UserId: user1.Id, 664 ChannelId: channel1.Id, 665 Message: "test", 666 Filenames: []string{ 667 fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png"), 668 fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"), 669 fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"), // duplicate a filename to recreate a rare bug 670 }, 671 })).(*model.Post) 672 673 if post1.FileIds != nil && len(post1.FileIds) > 0 { 674 t.Fatal("post shouldn't have file ids") 675 } else if post1.Filenames == nil || len(post1.Filenames) != 3 { 676 t.Fatal("post should have filenames") 677 } 678 679 // Indirectly call migrateFilenamesToFileInfos by calling Client.GetFileInfosForPost 680 var infos []*model.FileInfo 681 if infosResult, err := Client.GetFileInfosForPost(post1.ChannelId, post1.Id, ""); err != nil { 682 t.Fatal(err) 683 } else { 684 infos = infosResult 685 } 686 687 if len(infos) != 2 { 688 t.Log(infos) 689 t.Fatal("should've had 2 infos after migration") 690 } else if infos[0].Path != "" || infos[0].ThumbnailPath != "" || infos[0].PreviewPath != "" { 691 t.Fatal("shouldn't return paths to client") 692 } 693 694 // Should be able to get files after migration 695 if body, err := Client.GetFile(infos[0].Id); err != nil { 696 t.Fatal(err) 697 } else { 698 body.Close() 699 } 700 701 if body, err := Client.GetFile(infos[1].Id); err != nil { 702 t.Fatal(err) 703 } else { 704 body.Close() 705 } 706 707 // Make sure we aren't generating a new set of FileInfos on a second call to GetFileInfosForPost 708 if infos2 := Client.MustGeneric(Client.GetFileInfosForPost(post1.ChannelId, post1.Id, "")).([]*model.FileInfo); len(infos2) != len(infos) { 709 t.Fatal("should've received the same 2 infos after second call") 710 } else if (infos[0].Id != infos2[0].Id && infos[0].Id != infos2[1].Id) || (infos[1].Id != infos2[0].Id && infos[1].Id != infos2[1].Id) { 711 t.Fatal("should've returned the exact same 2 infos after second call") 712 } 713 714 if result, err := Client.GetPost(post1.ChannelId, post1.Id, ""); err != nil { 715 t.Fatal(err) 716 } else if post := result.Data.(*model.PostList).Posts[post1.Id]; len(post.Filenames) != 0 { 717 t.Fatal("post shouldn't have filenames") 718 } else if len(post.FileIds) != 2 { 719 t.Fatal("post should have 2 file ids") 720 } else if (infos[0].Id != post.FileIds[0] && infos[0].Id != post.FileIds[1]) || (infos[1].Id != post.FileIds[0] && infos[1].Id != post.FileIds[1]) { 721 t.Fatal("post file ids should match GetFileInfosForPost results") 722 } 723 } 724 725 func uploadFileOld(t *testing.T, data []byte, dest string, filename string) { 726 os.MkdirAll(dest, os.ModePerm) 727 eFile, err := os.Create(dest + "/" + filename) 728 if err != nil { 729 t.Fatal(err) 730 } 731 defer eFile.Close() 732 733 _, err = io.Copy(eFile, bytes.NewReader(data)) // first var shows number of bytes 734 if err != nil { 735 t.Fatal(err) 736 } 737 738 err = eFile.Sync() 739 if err != nil { 740 t.Fatal(err) 741 } 742 } 743 744 func TestFindTeamIdForFilename(t *testing.T) { 745 th := Setup().InitBasic() 746 defer th.TearDown() 747 748 if *th.App.Config().FileSettings.DriverName == "" { 749 t.Skip("skipping because no file driver is enabled") 750 } 751 752 Client := th.BasicClient 753 754 user1 := th.BasicUser 755 756 team1 := th.BasicTeam 757 team2 := th.CreateTeam(th.BasicClient) 758 759 channel1 := th.BasicChannel 760 761 Client.SetTeamId(team2.Id) 762 channel2 := Client.Must(Client.CreateChannel(&model.Channel{ 763 Name: model.NewId(), 764 Type: model.CHANNEL_OPEN, 765 // No TeamId set to simulate a direct channel 766 })).Data.(*model.Channel) 767 Client.SetTeamId(team1.Id) 768 769 var fileId1 string 770 var fileId2 string 771 data, err := readTestFile("test.png") 772 if err != nil { 773 t.Fatal(err) 774 } else { 775 fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 776 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png") 777 778 Client.SetTeamId(team2.Id) 779 fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel2.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 780 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team2.Id, channel2.Id, user1.Id, fileId2), "test.png") 781 Client.SetTeamId(team1.Id) 782 } 783 784 // Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post 785 post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{ 786 UserId: user1.Id, 787 ChannelId: channel1.Id, 788 Message: "test", 789 Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")}, 790 })).(*model.Post) 791 792 if teamId := th.App.FindTeamIdForFilename(post1, post1.Filenames[0]); teamId != team1.Id { 793 t.Log(teamId) 794 t.Fatal("file should've been found under team1") 795 } 796 797 Client.SetTeamId(team2.Id) 798 post2 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{ 799 UserId: user1.Id, 800 ChannelId: channel2.Id, 801 Message: "test", 802 Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel2.Id, user1.Id, fileId2, "test.png")}, 803 })).(*model.Post) 804 Client.SetTeamId(team1.Id) 805 806 if teamId := th.App.FindTeamIdForFilename(post2, post2.Filenames[0]); teamId != team2.Id { 807 t.Fatal("file should've been found under team2") 808 } 809 } 810 811 func TestGetInfoForFilename(t *testing.T) { 812 th := Setup().InitBasic() 813 defer th.TearDown() 814 815 if *th.App.Config().FileSettings.DriverName == "" { 816 t.Skip("skipping because no file driver is enabled") 817 } 818 819 Client := th.BasicClient 820 821 user1 := th.BasicUser 822 823 team1 := th.BasicTeam 824 825 channel1 := th.BasicChannel 826 827 var fileId1 string 828 var path string 829 var thumbnailPath string 830 var previewPath string 831 data, err := readTestFile("test.png") 832 if err != nil { 833 t.Fatal(err) 834 } else { 835 fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id 836 uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png") 837 path = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).Path 838 thumbnailPath = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).ThumbnailPath 839 previewPath = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).PreviewPath 840 } 841 842 // Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post 843 post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{ 844 UserId: user1.Id, 845 ChannelId: channel1.Id, 846 Message: "test", 847 Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")}, 848 })).(*model.Post) 849 850 date := time.Now().Format("20060102") 851 852 if info := th.App.GetInfoForFilename(post1, team1.Id, post1.Filenames[0]); info == nil { 853 t.Fatal("info shouldn't be nil") 854 } else if info.Id == "" { 855 t.Fatal("info.Id shouldn't be empty") 856 } else if info.CreatorId != user1.Id { 857 t.Fatal("incorrect user id") 858 } else if info.PostId != post1.Id { 859 t.Fatal("incorrect user id") 860 } else if fmt.Sprintf("%s/%s", date, info.Path) != path { 861 t.Fatal("incorrect path") 862 } else if fmt.Sprintf("%s/%s", date, info.ThumbnailPath) != thumbnailPath { 863 t.Fatal("incorrect thumbnail path") 864 } else if fmt.Sprintf("%s/%s", date, info.PreviewPath) != previewPath { 865 t.Fatal("incorrect preview path") 866 } else if info.Name != "test.png" { 867 t.Fatal("incorrect name") 868 } 869 } 870 871 func readTestFile(name string) ([]byte, error) { 872 path, _ := utils.FindDir("tests") 873 file, err := os.Open(path + "/" + name) 874 if err != nil { 875 return nil, err 876 } 877 defer file.Close() 878 879 data := &bytes.Buffer{} 880 if _, err := io.Copy(data, file); err != nil { 881 return nil, err 882 } else { 883 return data.Bytes(), nil 884 } 885 } 886 887 // Similar to s3.New() but allows initialization of signature v2 or signature v4 client. 888 // If signV2 input is false, function always returns signature v4. 889 // 890 // Additionally this function also takes a user defined region, if set 891 // disables automatic region lookup. 892 func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool, region string) (*s3.Client, error) { 893 var creds *credentials.Credentials 894 if signV2 { 895 creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV2) 896 } else { 897 creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV4) 898 } 899 return s3.NewWithCredentials(endpoint, creds, secure, region) 900 } 901 902 func cleanupTestFile(info *model.FileInfo, settings *model.FileSettings) error { 903 if *settings.DriverName == model.IMAGE_DRIVER_S3 { 904 endpoint := settings.AmazonS3Endpoint 905 accessKey := settings.AmazonS3AccessKeyId 906 secretKey := settings.AmazonS3SecretAccessKey 907 secure := *settings.AmazonS3SSL 908 signV2 := *settings.AmazonS3SignV2 909 region := settings.AmazonS3Region 910 s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region) 911 if err != nil { 912 return err 913 } 914 bucket := settings.AmazonS3Bucket 915 if err := s3Clnt.RemoveObject(bucket, info.Path); err != nil { 916 return err 917 } 918 919 if info.ThumbnailPath != "" { 920 if err := s3Clnt.RemoveObject(bucket, info.ThumbnailPath); err != nil { 921 return err 922 } 923 } 924 925 if info.PreviewPath != "" { 926 if err := s3Clnt.RemoveObject(bucket, info.PreviewPath); err != nil { 927 return err 928 } 929 } 930 } else if *settings.DriverName == model.IMAGE_DRIVER_LOCAL { 931 if err := os.Remove(settings.Directory + info.Path); err != nil { 932 return err 933 } 934 935 if info.ThumbnailPath != "" { 936 if err := os.Remove(settings.Directory + info.ThumbnailPath); err != nil { 937 return err 938 } 939 } 940 941 if info.PreviewPath != "" { 942 if err := os.Remove(settings.Directory + info.PreviewPath); err != nil { 943 return err 944 } 945 } 946 } 947 948 return nil 949 }