mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* Prepare file upload infrastructure for Data Retention. This commit prepares the file upload infrastructure for the data retention feature that is under construction. Changes are: * Move file management code to utils to allow access to it from jobs. * From now on, store all file uploads in a top level folder which is the date of the day on which they were uploaded. This commit is based on Harrison Healey's branch, but updated to work with the latest master. * Use NewAppError
924 lines
29 KiB
Go
924 lines
29 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/platform/app"
|
|
"github.com/mattermost/platform/model"
|
|
"github.com/mattermost/platform/store"
|
|
"github.com/mattermost/platform/utils"
|
|
|
|
s3 "github.com/minio/minio-go"
|
|
"github.com/minio/minio-go/pkg/credentials"
|
|
)
|
|
|
|
func TestUploadFile(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Logf("skipping because no file driver is enabled")
|
|
return
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
team := th.BasicTeam
|
|
user := th.BasicUser
|
|
channel := th.BasicChannel
|
|
|
|
var uploadInfo *model.FileInfo
|
|
if data, err := readTestFile("test.png"); err != nil {
|
|
t.Fatal(err)
|
|
} else if resp, err := Client.UploadPostAttachment(data, channel.Id, "test.png"); err != nil {
|
|
t.Fatal(err)
|
|
} else if len(resp.FileInfos) != 1 {
|
|
t.Fatal("should've returned a single file infos")
|
|
} else {
|
|
uploadInfo = resp.FileInfos[0]
|
|
}
|
|
|
|
// The returned file info from the upload call will be missing some fields that will be stored in the database
|
|
if uploadInfo.CreatorId != user.Id {
|
|
t.Fatal("file should be assigned to user")
|
|
} else if uploadInfo.PostId != "" {
|
|
t.Fatal("file shouldn't have a post")
|
|
} else if uploadInfo.Path != "" {
|
|
t.Fatal("file path should not be set on returned info")
|
|
} else if uploadInfo.ThumbnailPath != "" {
|
|
t.Fatal("file thumbnail path should not be set on returned info")
|
|
} else if uploadInfo.PreviewPath != "" {
|
|
t.Fatal("file preview path should not be set on returned info")
|
|
}
|
|
|
|
var info *model.FileInfo
|
|
if result := <-app.Srv.Store.FileInfo().Get(uploadInfo.Id); result.Err != nil {
|
|
t.Fatal(result.Err)
|
|
} else {
|
|
info = result.Data.(*model.FileInfo)
|
|
}
|
|
|
|
if info.Id != uploadInfo.Id {
|
|
t.Fatal("file id from response should match one stored in database")
|
|
} else if info.CreatorId != user.Id {
|
|
t.Fatal("file should be assigned to user")
|
|
} else if info.PostId != "" {
|
|
t.Fatal("file shouldn't have a post")
|
|
} else if info.Path == "" {
|
|
t.Fatal("file path should be set in database")
|
|
} else if info.ThumbnailPath == "" {
|
|
t.Fatal("file thumbnail path should be set in database")
|
|
} else if info.PreviewPath == "" {
|
|
t.Fatal("file preview path should be set in database")
|
|
}
|
|
|
|
date := time.Now().Format("20060102")
|
|
|
|
// This also makes sure that the relative path provided above is sanitized out
|
|
expectedPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test.png", date, team.Id, channel.Id, user.Id, info.Id)
|
|
if info.Path != expectedPath {
|
|
t.Logf("file is saved in %v", info.Path)
|
|
t.Fatalf("file should've been saved in %v", expectedPath)
|
|
}
|
|
|
|
expectedThumbnailPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_thumb.jpg", date, team.Id, channel.Id, user.Id, info.Id)
|
|
if info.ThumbnailPath != expectedThumbnailPath {
|
|
t.Logf("file thumbnail is saved in %v", info.ThumbnailPath)
|
|
t.Fatalf("file thumbnail should've been saved in %v", expectedThumbnailPath)
|
|
}
|
|
|
|
expectedPreviewPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_preview.jpg", date, team.Id, channel.Id, user.Id, info.Id)
|
|
if info.PreviewPath != expectedPreviewPath {
|
|
t.Logf("file preview is saved in %v", info.PreviewPath)
|
|
t.Fatalf("file preview should've been saved in %v", expectedPreviewPath)
|
|
}
|
|
|
|
enableFileAttachments := *utils.Cfg.FileSettings.EnableFileAttachments
|
|
defer func() {
|
|
*utils.Cfg.FileSettings.EnableFileAttachments = enableFileAttachments
|
|
}()
|
|
*utils.Cfg.FileSettings.EnableFileAttachments = false
|
|
|
|
if data, err := readTestFile("test.png"); err != nil {
|
|
t.Fatal(err)
|
|
} else if _, err = Client.UploadPostAttachment(data, channel.Id, "test.png"); err == nil {
|
|
t.Fatal("should have errored")
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if err := cleanupTestFile(info); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetFileInfo(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
user := th.BasicUser
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
if data, err := readTestFile("test.png"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
info, err := Client.GetFileInfo(fileId)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if info.Id != fileId {
|
|
t.Fatal("got incorrect file")
|
|
} else if info.CreatorId != user.Id {
|
|
t.Fatal("file should be assigned to user")
|
|
} else if info.PostId != "" {
|
|
t.Fatal("file shouldn't have a post")
|
|
} else if info.Path != "" {
|
|
t.Fatal("file path shouldn't have been returned to client")
|
|
} else if info.ThumbnailPath != "" {
|
|
t.Fatal("file thumbnail path shouldn't have been returned to client")
|
|
} else if info.PreviewPath != "" {
|
|
t.Fatal("file preview path shouldn't have been returned to client")
|
|
} else if info.MimeType != "image/png" {
|
|
t.Fatal("mime type should've been image/png")
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// Other user shouldn't be able to get file info for this file before it's attached to a post
|
|
th.LoginBasic2()
|
|
|
|
if _, err := Client.GetFileInfo(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file info before it's attached to a post")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
// Other user shouldn't be able to get file info for this file if they're not in the channel for it
|
|
if _, err := Client.GetFileInfo(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file info when not in channel")
|
|
}
|
|
|
|
Client.Must(Client.JoinChannel(channel.Id))
|
|
|
|
// Other user should now be able to get file info
|
|
if info2, err := Client.GetFileInfo(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else if info2.Id != fileId {
|
|
t.Fatal("other user got incorrect file")
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetFile(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if body, err := Client.GetFile(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
received, err := ioutil.ReadAll(body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if len(received) != len(data) {
|
|
t.Fatal("received file should be the same size as the sent one")
|
|
}
|
|
|
|
for i := range data {
|
|
if data[i] != received[i] {
|
|
t.Fatal("received file didn't match sent one")
|
|
}
|
|
}
|
|
|
|
body.Close()
|
|
}
|
|
|
|
// Other user shouldn't be able to get file for this file before it's attached to a post
|
|
th.LoginBasic2()
|
|
|
|
if _, err := Client.GetFile(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
// Other user shouldn't be able to get file for this file if they're not in the channel for it
|
|
if _, err := Client.GetFile(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file when not in channel")
|
|
}
|
|
|
|
Client.Must(Client.JoinChannel(channel.Id))
|
|
|
|
// Other user should now be able to get file
|
|
if body, err := Client.GetFile(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
received, err := ioutil.ReadAll(body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if len(received) != len(data) {
|
|
t.Fatal("received file should be the same size as the sent one")
|
|
}
|
|
|
|
for i := range data {
|
|
if data[i] != received[i] {
|
|
t.Fatal("received file didn't match sent one")
|
|
}
|
|
}
|
|
|
|
body.Close()
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetFileThumbnail(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if body, err := Client.GetFileThumbnail(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
// Other user shouldn't be able to get thumbnail for this file before it's attached to a post
|
|
th.LoginBasic2()
|
|
|
|
if _, err := Client.GetFileThumbnail(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
// Other user shouldn't be able to get thumbnail for this file if they're not in the channel for it
|
|
if _, err := Client.GetFileThumbnail(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file when not in channel")
|
|
}
|
|
|
|
Client.Must(Client.JoinChannel(channel.Id))
|
|
|
|
// Other user should now be able to get thumbnail
|
|
if body, err := Client.GetFileThumbnail(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetFilePreview(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if body, err := Client.GetFilePreview(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
// Other user shouldn't be able to get preview for this file before it's attached to a post
|
|
th.LoginBasic2()
|
|
|
|
if _, err := Client.GetFilePreview(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
// Other user shouldn't be able to get preview for this file if they're not in the channel for it
|
|
if _, err := Client.GetFilePreview(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file when not in channel")
|
|
}
|
|
|
|
Client.Must(Client.JoinChannel(channel.Id))
|
|
|
|
// Other user should now be able to get preview
|
|
if body, err := Client.GetFilePreview(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetPublicFile(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
enablePublicLink := utils.Cfg.FileSettings.EnablePublicLink
|
|
publicLinkSalt := *utils.Cfg.FileSettings.PublicLinkSalt
|
|
defer func() {
|
|
utils.Cfg.FileSettings.EnablePublicLink = enablePublicLink
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = publicLinkSalt
|
|
}()
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = model.NewId()
|
|
|
|
Client := th.BasicClient
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
link := Client.MustGeneric(Client.GetPublicLink(fileId)).(string)
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK {
|
|
t.Log(link)
|
|
t.Fatal("failed to get image with public link", err)
|
|
}
|
|
|
|
if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link without hash", resp.Status)
|
|
}
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = false
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented {
|
|
t.Fatal("should've failed to get image with disabled public link")
|
|
}
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
|
|
// test after the salt has changed
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = model.NewId()
|
|
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link after salt changed")
|
|
}
|
|
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link after salt changed")
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetPublicFileOld(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
enablePublicLink := utils.Cfg.FileSettings.EnablePublicLink
|
|
publicLinkSalt := *utils.Cfg.FileSettings.PublicLinkSalt
|
|
defer func() {
|
|
utils.Cfg.FileSettings.EnablePublicLink = enablePublicLink
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = publicLinkSalt
|
|
}()
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = model.NewId()
|
|
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
//fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
fileId = model.NewId()
|
|
fileInfo := model.FileInfo{
|
|
Id: fileId,
|
|
CreateAt: model.GetMillis(),
|
|
CreatorId: th.BasicUser.Id,
|
|
Path: fmt.Sprintf("teams/%s/channels/%s/users/%s/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId, "test.png"),
|
|
}
|
|
store.Must(app.Srv.Store.FileInfo().Save(&fileInfo))
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId), "test.png")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
// reconstruct old style of link
|
|
siteURL := *utils.Cfg.ServiceSettings.SiteURL
|
|
if siteURL == "" {
|
|
siteURL = "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress
|
|
}
|
|
link := generatePublicLinkOld(siteURL, th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId+"/test.png")
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("failed to get image with public link err=%v resp=%v", err, resp)
|
|
}
|
|
|
|
if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link without hash", resp.Status)
|
|
}
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = false
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented {
|
|
t.Fatal("should've failed to get image with disabled public link")
|
|
}
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
|
|
// test after the salt has changed
|
|
*utils.Cfg.FileSettings.PublicLinkSalt = model.NewId()
|
|
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link after salt changed")
|
|
}
|
|
|
|
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatal("should've failed to get image with public link after salt changed")
|
|
}
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func generatePublicLinkOld(siteURL, teamId, channelId, userId, filename string) string {
|
|
hash := app.GeneratePublicLinkHash(filename, *utils.Cfg.FileSettings.PublicLinkSalt)
|
|
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)
|
|
}
|
|
|
|
func TestGetPublicLink(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
enablePublicLink := utils.Cfg.FileSettings.EnablePublicLink
|
|
defer func() {
|
|
utils.Cfg.FileSettings.EnablePublicLink = enablePublicLink
|
|
}()
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
|
|
Client := th.BasicClient
|
|
channel := th.BasicChannel
|
|
|
|
var fileId string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
}
|
|
|
|
if _, err := Client.GetPublicLink(fileId); err == nil {
|
|
t.Fatal("should've failed to get public link before file is attached to a post")
|
|
}
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
store.Must(app.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = false
|
|
|
|
if _, err := Client.GetPublicLink(fileId); err == nil {
|
|
t.Fatal("should've failed to get public link when disabled")
|
|
}
|
|
|
|
utils.Cfg.FileSettings.EnablePublicLink = true
|
|
|
|
if link, err := Client.GetPublicLink(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else if link == "" {
|
|
t.Fatal("should've received public link")
|
|
}
|
|
|
|
// Other user shouldn't be able to get public link for this file if they're not in the channel for it
|
|
th.LoginBasic2()
|
|
|
|
if _, err := Client.GetPublicLink(fileId); err == nil {
|
|
t.Fatal("other user shouldn't be able to get file when not in channel")
|
|
}
|
|
|
|
Client.Must(Client.JoinChannel(channel.Id))
|
|
|
|
// Other user should now be able to get public link
|
|
if link, err := Client.GetPublicLink(fileId); err != nil {
|
|
t.Fatal(err)
|
|
} else if link == "" {
|
|
t.Fatal("should've received public link")
|
|
}
|
|
|
|
// Wait a bit for files to ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
if err := cleanupTestFile(store.Must(app.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestMigrateFilenamesToFileInfos(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
|
|
user1 := th.BasicUser
|
|
|
|
channel1 := Client.Must(Client.CreateChannel(&model.Channel{
|
|
Name: model.NewId(),
|
|
Type: model.CHANNEL_OPEN,
|
|
// No TeamId set to simulate a direct channel
|
|
})).Data.(*model.Channel)
|
|
|
|
var fileId1 string
|
|
var fileId2 string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
|
fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId2), "test.png")
|
|
}
|
|
|
|
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
|
post1 := store.Must(app.Srv.Store.Post().Save(&model.Post{
|
|
UserId: user1.Id,
|
|
ChannelId: channel1.Id,
|
|
Message: "test",
|
|
Filenames: []string{
|
|
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png"),
|
|
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"),
|
|
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"), // duplicate a filename to recreate a rare bug
|
|
},
|
|
})).(*model.Post)
|
|
|
|
if post1.FileIds != nil && len(post1.FileIds) > 0 {
|
|
t.Fatal("post shouldn't have file ids")
|
|
} else if post1.Filenames == nil || len(post1.Filenames) != 3 {
|
|
t.Fatal("post should have filenames")
|
|
}
|
|
|
|
// Indirectly call migrateFilenamesToFileInfos by calling Client.GetFileInfosForPost
|
|
var infos []*model.FileInfo
|
|
if infosResult, err := Client.GetFileInfosForPost(post1.ChannelId, post1.Id, ""); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
infos = infosResult
|
|
}
|
|
|
|
if len(infos) != 2 {
|
|
t.Log(infos)
|
|
t.Fatal("should've had 2 infos after migration")
|
|
} else if infos[0].Path != "" || infos[0].ThumbnailPath != "" || infos[0].PreviewPath != "" {
|
|
t.Fatal("shouldn't return paths to client")
|
|
}
|
|
|
|
// Should be able to get files after migration
|
|
if body, err := Client.GetFile(infos[0].Id); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
if body, err := Client.GetFile(infos[1].Id); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
body.Close()
|
|
}
|
|
|
|
// Make sure we aren't generating a new set of FileInfos on a second call to GetFileInfosForPost
|
|
if infos2 := Client.MustGeneric(Client.GetFileInfosForPost(post1.ChannelId, post1.Id, "")).([]*model.FileInfo); len(infos2) != len(infos) {
|
|
t.Fatal("should've received the same 2 infos after second call")
|
|
} 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) {
|
|
t.Fatal("should've returned the exact same 2 infos after second call")
|
|
}
|
|
|
|
if result, err := Client.GetPost(post1.ChannelId, post1.Id, ""); err != nil {
|
|
t.Fatal(err)
|
|
} else if post := result.Data.(*model.PostList).Posts[post1.Id]; len(post.Filenames) != 0 {
|
|
t.Fatal("post shouldn't have filenames")
|
|
} else if len(post.FileIds) != 2 {
|
|
t.Fatal("post should have 2 file ids")
|
|
} 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]) {
|
|
t.Fatal("post file ids should match GetFileInfosForPost results")
|
|
}
|
|
}
|
|
|
|
func uploadFileOld(t *testing.T, data []byte, dest string, filename string) {
|
|
os.MkdirAll(dest, os.ModePerm)
|
|
eFile, err := os.Create(dest + "/" + filename)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer eFile.Close()
|
|
|
|
_, err = io.Copy(eFile, bytes.NewReader(data)) // first var shows number of bytes
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = eFile.Sync()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestFindTeamIdForFilename(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
|
|
user1 := th.BasicUser
|
|
|
|
team1 := th.BasicTeam
|
|
team2 := th.CreateTeam(th.BasicClient)
|
|
|
|
channel1 := th.BasicChannel
|
|
|
|
Client.SetTeamId(team2.Id)
|
|
channel2 := Client.Must(Client.CreateChannel(&model.Channel{
|
|
Name: model.NewId(),
|
|
Type: model.CHANNEL_OPEN,
|
|
// No TeamId set to simulate a direct channel
|
|
})).Data.(*model.Channel)
|
|
Client.SetTeamId(team1.Id)
|
|
|
|
var fileId1 string
|
|
var fileId2 string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
|
|
|
Client.SetTeamId(team2.Id)
|
|
fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel2.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team2.Id, channel2.Id, user1.Id, fileId2), "test.png")
|
|
Client.SetTeamId(team1.Id)
|
|
}
|
|
|
|
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
|
post1 := store.Must(app.Srv.Store.Post().Save(&model.Post{
|
|
UserId: user1.Id,
|
|
ChannelId: channel1.Id,
|
|
Message: "test",
|
|
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")},
|
|
})).(*model.Post)
|
|
|
|
if teamId := app.FindTeamIdForFilename(post1, post1.Filenames[0]); teamId != team1.Id {
|
|
t.Log(teamId)
|
|
t.Fatal("file should've been found under team1")
|
|
}
|
|
|
|
Client.SetTeamId(team2.Id)
|
|
post2 := store.Must(app.Srv.Store.Post().Save(&model.Post{
|
|
UserId: user1.Id,
|
|
ChannelId: channel2.Id,
|
|
Message: "test",
|
|
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel2.Id, user1.Id, fileId2, "test.png")},
|
|
})).(*model.Post)
|
|
Client.SetTeamId(team1.Id)
|
|
|
|
if teamId := app.FindTeamIdForFilename(post2, post2.Filenames[0]); teamId != team2.Id {
|
|
t.Fatal("file should've been found under team2")
|
|
}
|
|
}
|
|
|
|
func TestGetInfoForFilename(t *testing.T) {
|
|
th := Setup().InitBasic()
|
|
|
|
if utils.Cfg.FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
Client := th.BasicClient
|
|
|
|
user1 := th.BasicUser
|
|
|
|
team1 := th.BasicTeam
|
|
|
|
channel1 := th.BasicChannel
|
|
|
|
var fileId1 string
|
|
var path string
|
|
var thumbnailPath string
|
|
var previewPath string
|
|
data, err := readTestFile("test.png")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
|
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
|
path = store.Must(app.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).Path
|
|
thumbnailPath = store.Must(app.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).ThumbnailPath
|
|
previewPath = store.Must(app.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).PreviewPath
|
|
}
|
|
|
|
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
|
post1 := store.Must(app.Srv.Store.Post().Save(&model.Post{
|
|
UserId: user1.Id,
|
|
ChannelId: channel1.Id,
|
|
Message: "test",
|
|
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")},
|
|
})).(*model.Post)
|
|
|
|
date := time.Now().Format("20060102")
|
|
|
|
if info := app.GetInfoForFilename(post1, team1.Id, post1.Filenames[0]); info == nil {
|
|
t.Fatal("info shouldn't be nil")
|
|
} else if info.Id == "" {
|
|
t.Fatal("info.Id shouldn't be empty")
|
|
} else if info.CreatorId != user1.Id {
|
|
t.Fatal("incorrect user id")
|
|
} else if info.PostId != post1.Id {
|
|
t.Fatal("incorrect user id")
|
|
} else if fmt.Sprintf("%s/%s", date, info.Path) != path {
|
|
t.Fatal("incorrect path")
|
|
} else if fmt.Sprintf("%s/%s", date, info.ThumbnailPath) != thumbnailPath {
|
|
t.Fatal("incorrect thumbnail path")
|
|
} else if fmt.Sprintf("%s/%s", date, info.PreviewPath) != previewPath {
|
|
t.Fatal("incorrect preview path")
|
|
} else if info.Name != "test.png" {
|
|
t.Fatal("incorrect name")
|
|
}
|
|
}
|
|
|
|
func readTestFile(name string) ([]byte, error) {
|
|
path, _ := utils.FindDir("tests")
|
|
file, err := os.Open(path + "/" + name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
data := &bytes.Buffer{}
|
|
if _, err := io.Copy(data, file); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return data.Bytes(), nil
|
|
}
|
|
}
|
|
|
|
// Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
|
|
// If signV2 input is false, function always returns signature v4.
|
|
//
|
|
// Additionally this function also takes a user defined region, if set
|
|
// disables automatic region lookup.
|
|
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool, region string) (*s3.Client, error) {
|
|
var creds *credentials.Credentials
|
|
if signV2 {
|
|
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV2)
|
|
} else {
|
|
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV4)
|
|
}
|
|
return s3.NewWithCredentials(endpoint, creds, secure, region)
|
|
}
|
|
|
|
func cleanupTestFile(info *model.FileInfo) error {
|
|
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
|
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
|
region := utils.Cfg.FileSettings.AmazonS3Region
|
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bucket := utils.Cfg.FileSettings.AmazonS3Bucket
|
|
if err := s3Clnt.RemoveObject(bucket, info.Path); err != nil {
|
|
return err
|
|
}
|
|
|
|
if info.ThumbnailPath != "" {
|
|
if err := s3Clnt.RemoveObject(bucket, info.ThumbnailPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if info.PreviewPath != "" {
|
|
if err := s3Clnt.RemoveObject(bucket, info.PreviewPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
|
|
if err := os.Remove(utils.Cfg.FileSettings.Directory + info.Path); err != nil {
|
|
return err
|
|
}
|
|
|
|
if info.ThumbnailPath != "" {
|
|
if err := os.Remove(utils.Cfg.FileSettings.Directory + info.ThumbnailPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if info.PreviewPath != "" {
|
|
if err := os.Remove(utils.Cfg.FileSettings.Directory + info.PreviewPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|