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