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  }