github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/api/file_test.go (about)

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