mirror of
https://github.com/mattermost/mattermost.git
synced 2026-04-13 04:57:45 -04:00
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
* Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
1712 lines
63 KiB
Go
1712 lines
63 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package api4
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/textproto"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/v8/channels/app"
|
|
"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
|
|
"github.com/mattermost/mattermost/server/v8/channels/utils/testutils"
|
|
)
|
|
|
|
var testDir = ""
|
|
|
|
func init() {
|
|
testDir, _ = fileutils.FindDir("tests")
|
|
}
|
|
|
|
// File Section
|
|
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
|
|
|
func escapeQuotes(s string) string {
|
|
return quoteEscaper.Replace(s)
|
|
}
|
|
|
|
func randomBytes(t *testing.T, n int) []byte {
|
|
bb := make([]byte, n)
|
|
_, err := rand.Read(bb)
|
|
require.NoError(t, err)
|
|
return bb
|
|
}
|
|
|
|
func fileBytes(t *testing.T, path string) []byte {
|
|
path = filepath.Join(testDir, path)
|
|
f, err := os.Open(path)
|
|
require.NoError(t, err)
|
|
defer f.Close()
|
|
bb, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
return bb
|
|
}
|
|
|
|
func testDoUploadFileRequest(tb testing.TB, c *model.Client4, url string, blob []byte, contentType string,
|
|
contentLength int64,
|
|
) (*model.FileUploadResponse, *model.Response, error) {
|
|
req, err := http.NewRequest("POST", c.APIURL+"/files"+url, bytes.NewReader(blob))
|
|
require.NoError(tb, err)
|
|
|
|
if contentLength != 0 {
|
|
req.ContentLength = contentLength
|
|
}
|
|
req.Header.Set("Content-Type", contentType)
|
|
if c.AuthToken != "" {
|
|
req.Header.Set(model.HeaderAuth, c.AuthType+" "+c.AuthToken)
|
|
}
|
|
|
|
resp, err := c.HTTPClient.Do(req)
|
|
require.NoError(tb, err)
|
|
require.NotNil(tb, resp)
|
|
defer closeBody(resp)
|
|
|
|
if resp.StatusCode >= 300 {
|
|
return nil, model.BuildResponse(resp), model.AppErrorFromJSON(resp.Body)
|
|
}
|
|
|
|
var res model.FileUploadResponse
|
|
if jsonErr := json.NewDecoder(resp.Body).Decode(&res); jsonErr != nil {
|
|
return nil, nil, model.NewAppError("doUploadFile", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
|
}
|
|
return &res, model.BuildResponse(resp), nil
|
|
}
|
|
|
|
func testUploadFilesPost(
|
|
tb testing.TB,
|
|
c *model.Client4,
|
|
channelId string,
|
|
names []string,
|
|
blobs [][]byte,
|
|
clientIds []string,
|
|
useChunked bool,
|
|
isBookmark bool,
|
|
) (*model.FileUploadResponse, *model.Response, error) {
|
|
// Do not check len(clientIds), leave it entirely to the user to
|
|
// provide. The server will error out if it does not match the number
|
|
// of files, but it's not critical here.
|
|
require.NotEmpty(tb, names)
|
|
require.NotEmpty(tb, blobs)
|
|
require.Equal(tb, len(names), len(blobs))
|
|
|
|
fileUploadResponse := &model.FileUploadResponse{}
|
|
for i, blob := range blobs {
|
|
var cl int64
|
|
if useChunked {
|
|
cl = -1
|
|
} else {
|
|
cl = int64(len(blob))
|
|
}
|
|
ct := http.DetectContentType(blob)
|
|
|
|
postURL := fmt.Sprintf("?channel_id=%v", url.QueryEscape(channelId)) +
|
|
fmt.Sprintf("&filename=%v", url.QueryEscape(names[i]))
|
|
if len(clientIds) > i {
|
|
postURL += fmt.Sprintf("&client_id=%v", url.QueryEscape(clientIds[i]))
|
|
}
|
|
|
|
if isBookmark {
|
|
postURL += "&bookmark=true"
|
|
}
|
|
|
|
fur, resp, err := testDoUploadFileRequest(tb, c, postURL, blob, ct, cl)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
fileUploadResponse.FileInfos = append(fileUploadResponse.FileInfos, fur.FileInfos[0])
|
|
if len(clientIds) > 0 {
|
|
if len(fur.ClientIds) > 0 {
|
|
fileUploadResponse.ClientIds = append(fileUploadResponse.ClientIds, fur.ClientIds[0])
|
|
} else {
|
|
fileUploadResponse.ClientIds = append(fileUploadResponse.ClientIds, "")
|
|
}
|
|
}
|
|
}
|
|
|
|
return fileUploadResponse, nil, nil
|
|
}
|
|
|
|
func testUploadFilesMultipart(
|
|
tb testing.TB,
|
|
c *model.Client4,
|
|
channelId string,
|
|
names []string,
|
|
blobs [][]byte,
|
|
clientIds []string,
|
|
isBookmark bool,
|
|
) (
|
|
*model.FileUploadResponse,
|
|
*model.Response,
|
|
error,
|
|
) {
|
|
// Do not check len(clientIds), leave it entirely to the user to
|
|
// provide. The server will error out if it does not match the number
|
|
// of files, but it's not critical here.
|
|
require.NotEmpty(tb, names)
|
|
require.NotEmpty(tb, blobs)
|
|
require.Equal(tb, len(names), len(blobs))
|
|
|
|
mwBody := &bytes.Buffer{}
|
|
mw := multipart.NewWriter(mwBody)
|
|
|
|
err := mw.WriteField("channel_id", channelId)
|
|
require.NoError(tb, err)
|
|
|
|
for i, blob := range blobs {
|
|
ct := http.DetectContentType(blob)
|
|
if len(clientIds) > i {
|
|
err = mw.WriteField("client_ids", clientIds[i])
|
|
require.NoError(tb, err)
|
|
}
|
|
|
|
h := textproto.MIMEHeader{}
|
|
h.Set("Content-Disposition",
|
|
fmt.Sprintf(`form-data; name="files"; filename="%s"`, escapeQuotes(names[i])))
|
|
h.Set("Content-Type", ct)
|
|
|
|
// If we error here, writing to mw, the deferred handler
|
|
var part io.Writer
|
|
part, err = mw.CreatePart(h)
|
|
require.NoError(tb, err)
|
|
|
|
_, err = io.Copy(part, bytes.NewReader(blob))
|
|
require.NoError(tb, err)
|
|
}
|
|
|
|
require.NoError(tb, mw.Close())
|
|
url := ""
|
|
if isBookmark {
|
|
url += "?bookmark=true"
|
|
}
|
|
fur, resp, err := testDoUploadFileRequest(tb, c, url, mwBody.Bytes(), mw.FormDataContentType(), -1)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
return fur, resp, nil
|
|
}
|
|
|
|
func TestUploadFiles(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
channel := th.BasicChannel
|
|
date := time.Now().Format("20060102")
|
|
|
|
// Get better error messages
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
|
|
|
|
tests := []struct {
|
|
title string
|
|
client *model.Client4
|
|
blobs [][]byte
|
|
names []string
|
|
clientIds []string
|
|
|
|
skipSuccessValidation bool
|
|
skipPayloadValidation bool
|
|
skipSimplePost bool
|
|
skipMultipart bool
|
|
channelId string
|
|
useChunkedInSimplePost bool
|
|
expectedCreatorId string
|
|
expectedPayloadNames []string
|
|
expectImage bool
|
|
expectedImageWidths []int
|
|
expectedImageHeights []int
|
|
expectedImageThumbnailNames []string
|
|
expectedImagePreviewNames []string
|
|
expectedImageHasPreview []bool
|
|
expectedImageMiniPreview []bool
|
|
setupConfig func(a *app.App) func(a *app.App)
|
|
checkResponse func(tb testing.TB, resp *model.Response)
|
|
uploadAsBookmark bool
|
|
}{
|
|
// Upload a bunch of files, mixed images and non-images
|
|
{
|
|
title: "Happy",
|
|
names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageMiniPreview: []bool{true, true, false, false, true},
|
|
},
|
|
// Upload a bunch of files, with clientIds
|
|
{
|
|
title: "Happy client_ids",
|
|
names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
|
|
clientIds: []string{"1", "2", "3", "4", "5"},
|
|
expectedImageMiniPreview: []bool{true, true, false, false, true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// Upload a bunch of images. testgif.gif is an animated GIF,
|
|
// so it does not have HasPreviewImage set.
|
|
{
|
|
title: "Happy images",
|
|
names: []string{"test.png", "testgif.gif"},
|
|
expectImage: true,
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageWidths: []int{408, 118},
|
|
expectedImageHeights: []int{336, 118},
|
|
expectedImageHasPreview: []bool{true, false},
|
|
expectedImageMiniPreview: []bool{true, true},
|
|
},
|
|
{
|
|
title: "Happy invalid image",
|
|
names: []string{"testgif.gif"},
|
|
blobs: [][]byte{fileBytes(t, "test-search.md")},
|
|
skipPayloadValidation: true,
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageMiniPreview: []bool{false},
|
|
},
|
|
// Simple POST, chunked encoding
|
|
{
|
|
title: "Happy image chunked post",
|
|
skipMultipart: true,
|
|
useChunkedInSimplePost: true,
|
|
names: []string{"test.png"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{408},
|
|
expectedImageHeights: []int{336},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageMiniPreview: []bool{true},
|
|
},
|
|
// Image thumbnail and preview: size and orientation. Note that
|
|
// the expected image dimensions remain the same regardless of the
|
|
// orientation - what we save in FileInfo is used by the
|
|
// clients to size UI elements, so the dimensions are "actual".
|
|
{
|
|
title: "Happy image thumbnail/preview 1",
|
|
names: []string{"orientation_test_1.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_1_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_1_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageMiniPreview: []bool{true},
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 2",
|
|
names: []string{"orientation_test_2.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_2_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_2_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 3",
|
|
names: []string{"orientation_test_3.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_3_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_3_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 4",
|
|
names: []string{"orientation_test_4.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_4_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_4_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 5",
|
|
names: []string{"orientation_test_5.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_5_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_5_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 6",
|
|
names: []string{"orientation_test_6.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_6_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_6_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 7",
|
|
names: []string{"orientation_test_7.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_7_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_7_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy image thumbnail/preview 8",
|
|
names: []string{"orientation_test_8.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_8_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_8_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// 5MB+ JPEG
|
|
{
|
|
title: "Happy image thumbnail/preview 5MB+",
|
|
names: []string{"orientation_test_9.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_9_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_9_expected_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{4000},
|
|
expectedImageHeights: []int{2667},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// TIFF preview test
|
|
{
|
|
title: "Happy image thumbnail/preview 9",
|
|
names: []string{"test.tiff"},
|
|
expectedImageThumbnailNames: []string{"test_expected_tiff_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"test_expected_tiff_preview.jpeg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{701},
|
|
expectedImageHeights: []int{701},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// Extremely wide image test
|
|
{
|
|
title: "Happy image thumbnail/preview 10",
|
|
names: []string{"10000x1.png"},
|
|
expectedImageThumbnailNames: []string{"10000x1_expected_thumb.png"},
|
|
expectedImagePreviewNames: []string{"10000x1_expected_preview.png"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{10000},
|
|
expectedImageHeights: []int{1},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// Extremely high image test
|
|
{
|
|
title: "Happy image thumbnail/preview 11",
|
|
names: []string{"1x10000.png"},
|
|
expectedImageThumbnailNames: []string{"1x10000_expected_thumb.png"},
|
|
expectedImagePreviewNames: []string{"1x10000_expected_preview.png"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{1},
|
|
expectedImageHeights: []int{10000},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// animated GIF
|
|
{
|
|
title: "Happy image thumbnail/preview 12",
|
|
names: []string{"testgif.gif"},
|
|
expectedImageThumbnailNames: []string{"testgif_expected_thumbnail.jpg"},
|
|
expectedImagePreviewNames: []string{"testgif_expected_preview.jpg"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{118},
|
|
expectedImageHeights: []int{118},
|
|
expectedImageHasPreview: []bool{false},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
{
|
|
title: "Happy admin",
|
|
client: th.SystemAdminClient,
|
|
names: []string{"test.png"},
|
|
expectedImageMiniPreview: []bool{true},
|
|
expectedCreatorId: th.SystemAdminUser.Id,
|
|
},
|
|
{
|
|
title: "Happy stream",
|
|
useChunkedInSimplePost: true,
|
|
skipPayloadValidation: true,
|
|
names: []string{"1Mb-stream"},
|
|
blobs: [][]byte{randomBytes(t, 1024*1024)},
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
maxFileSize := *a.Config().FileSettings.MaxFileSize
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 1024 * 1024 })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
|
|
}
|
|
},
|
|
expectedImageMiniPreview: []bool{false},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
},
|
|
// Webp image test
|
|
{
|
|
title: "Webp image",
|
|
names: []string{"testwebp.webp"},
|
|
expectImage: true,
|
|
expectedImageWidths: []int{128},
|
|
expectedImageHeights: []int{256},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedCreatorId: th.BasicUser.Id,
|
|
expectedImageMiniPreview: []bool{true},
|
|
},
|
|
// Error cases
|
|
{
|
|
title: "Error channel_id does not exist",
|
|
channelId: model.NewId(),
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckForbiddenStatus,
|
|
},
|
|
{
|
|
// on simple post this uploads the last file
|
|
// successfully, without a ClientId
|
|
title: "Error too few client_ids",
|
|
skipSimplePost: true,
|
|
names: []string{"test.png", "testplugin.tar.gz", "test-search.md"},
|
|
clientIds: []string{"1", "4"},
|
|
expectedImageMiniPreview: []bool{true, false, false},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckBadRequestStatus,
|
|
},
|
|
{
|
|
title: "Error invalid channel_id",
|
|
channelId: "../../junk",
|
|
names: []string{"test.png"},
|
|
expectedImageMiniPreview: []bool{true},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckBadRequestStatus,
|
|
},
|
|
{
|
|
title: "Error admin channel_id does not exist",
|
|
client: th.SystemAdminClient,
|
|
channelId: model.NewId(),
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckForbiddenStatus,
|
|
},
|
|
{
|
|
title: "Error admin invalid channel_id",
|
|
client: th.SystemAdminClient,
|
|
channelId: "../../junk",
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckBadRequestStatus,
|
|
},
|
|
{
|
|
title: "Error admin disabled uploads",
|
|
client: th.SystemAdminClient,
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckForbiddenStatus,
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
enableFileAttachments := *a.Config().FileSettings.EnableFileAttachments
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = false })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = enableFileAttachments })
|
|
}
|
|
},
|
|
},
|
|
{
|
|
title: "Error file too large",
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckRequestEntityTooLargeStatus,
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
maxFileSize := *a.Config().FileSettings.MaxFileSize
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
|
|
}
|
|
},
|
|
},
|
|
// File too large (chunked, simple POST only, multipart would've been redundant with above)
|
|
{
|
|
title: "File too large chunked",
|
|
useChunkedInSimplePost: true,
|
|
skipMultipart: true,
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckRequestEntityTooLargeStatus,
|
|
expectedImageMiniPreview: []bool{false},
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
maxFileSize := *a.Config().FileSettings.MaxFileSize
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
|
|
}
|
|
},
|
|
},
|
|
{
|
|
title: "Error stream too large",
|
|
skipPayloadValidation: true,
|
|
names: []string{"1Mb-stream"},
|
|
blobs: [][]byte{randomBytes(t, 1024*1024)},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckRequestEntityTooLargeStatus,
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
maxFileSize := *a.Config().FileSettings.MaxFileSize
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 10 * 1024 })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
|
|
}
|
|
},
|
|
},
|
|
|
|
{
|
|
title: "Error image too large",
|
|
names: []string{"test.png"},
|
|
skipSuccessValidation: true,
|
|
checkResponse: CheckBadRequestStatus,
|
|
setupConfig: func(a *app.App) func(a *app.App) {
|
|
maxResSize := *a.Config().FileSettings.MaxImageResolution
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = 90000 })
|
|
return func(a *app.App) {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = maxResSize })
|
|
}
|
|
},
|
|
},
|
|
{
|
|
title: "Bookmark images",
|
|
names: []string{"orientation_test_5.jpeg"},
|
|
expectedImageThumbnailNames: []string{"orientation_test_5_expected_thumb.jpeg"},
|
|
expectedImagePreviewNames: []string{"orientation_test_5_expected_preview.jpeg"},
|
|
channelId: channel.Id,
|
|
expectImage: true,
|
|
expectedCreatorId: model.BookmarkFileOwner,
|
|
expectedImageWidths: []int{2860},
|
|
expectedImageHeights: []int{1578},
|
|
expectedImageHasPreview: []bool{true},
|
|
expectedImageMiniPreview: []bool{true},
|
|
uploadAsBookmark: true,
|
|
},
|
|
}
|
|
|
|
for _, useMultipart := range []bool{true, false} {
|
|
for _, tc := range tests {
|
|
if tc.skipMultipart && useMultipart || tc.skipSimplePost && !useMultipart {
|
|
continue
|
|
}
|
|
|
|
title := ""
|
|
if useMultipart {
|
|
title = "multipart "
|
|
} else {
|
|
title = "simple "
|
|
}
|
|
if tc.title != "" {
|
|
title += tc.title + " "
|
|
}
|
|
title += fmt.Sprintf("%v", tc.names)
|
|
|
|
t.Run(title, func(t *testing.T) {
|
|
// Apply any necessary config changes
|
|
if tc.setupConfig != nil {
|
|
restoreConfig := tc.setupConfig(th.App)
|
|
if restoreConfig != nil {
|
|
defer restoreConfig(th.App)
|
|
}
|
|
}
|
|
|
|
// Set the default values
|
|
client := th.Client
|
|
if tc.client != nil {
|
|
client = tc.client
|
|
}
|
|
channelId := channel.Id
|
|
if tc.channelId != "" {
|
|
channelId = tc.channelId
|
|
}
|
|
|
|
blobs := tc.blobs
|
|
if len(blobs) == 0 {
|
|
for _, name := range tc.names {
|
|
blobs = append(blobs, fileBytes(t, name))
|
|
}
|
|
}
|
|
|
|
var fileResp *model.FileUploadResponse
|
|
var resp *model.Response
|
|
var err error
|
|
if useMultipart {
|
|
fileResp, resp, err = testUploadFilesMultipart(t, client, channelId, tc.names, blobs, tc.clientIds, tc.uploadAsBookmark)
|
|
} else {
|
|
fileResp, resp, err = testUploadFilesPost(t, client, channelId, tc.names, blobs, tc.clientIds, tc.useChunkedInSimplePost, tc.uploadAsBookmark)
|
|
}
|
|
|
|
if tc.checkResponse != nil {
|
|
tc.checkResponse(t, resp)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
if tc.skipSuccessValidation {
|
|
return
|
|
}
|
|
|
|
require.NotNil(t, fileResp, "Nil fileResp")
|
|
require.NotEqual(t, 0, len(fileResp.FileInfos), "Empty FileInfos")
|
|
require.Equal(t, len(tc.names), len(fileResp.FileInfos), "Mismatched actual or expected FileInfos")
|
|
|
|
for i, ri := range fileResp.FileInfos {
|
|
// The returned file info from the upload call will be missing some fields that will be stored in the database
|
|
assert.Equal(t, ri.CreatorId, tc.expectedCreatorId, "File should be assigned to user")
|
|
assert.Equal(t, ri.PostId, "", "File shouldn't have a post Id")
|
|
assert.Equal(t, ri.Path, "", "File path should not be set on returned info")
|
|
assert.Equal(t, ri.ThumbnailPath, "", "File thumbnail path should not be set on returned info")
|
|
assert.Equal(t, ri.PreviewPath, "", "File preview path should not be set on returned info")
|
|
if len(tc.expectedImageMiniPreview) == len(fileResp.FileInfos) {
|
|
assert.Equal(t, ri.MiniPreview != nil, tc.expectedImageMiniPreview[i], "File: %s mini preview state unexpected", tc.names[i])
|
|
}
|
|
if len(tc.clientIds) > i {
|
|
assert.True(t, len(fileResp.ClientIds) == len(tc.clientIds),
|
|
fmt.Sprintf("Wrong number of clientIds returned, expected %v, got %v", len(tc.clientIds), len(fileResp.ClientIds)))
|
|
assert.Equal(t, fileResp.ClientIds[i], tc.clientIds[i],
|
|
fmt.Sprintf("Wrong clientId returned, expected %v, got %v", tc.clientIds[i], fileResp.ClientIds[i]))
|
|
}
|
|
|
|
dbInfo, err := th.App.Srv().Store().FileInfo().Get(ri.Id)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, dbInfo.Id, ri.Id, "File id from response should match one stored in database")
|
|
assert.Equal(t, dbInfo.CreatorId, tc.expectedCreatorId, "F ile should be assigned to user")
|
|
assert.Equal(t, dbInfo.PostId, "", "File shouldn't have a post")
|
|
assert.NotEqual(t, dbInfo.Path, "", "File path should be set in database")
|
|
_, fname := filepath.Split(dbInfo.Path)
|
|
ext := filepath.Ext(fname)
|
|
name := fname[:len(fname)-len(ext)]
|
|
expectedDir := fmt.Sprintf("%v/teams/%v/channels/%v/users/%s/%s", date, FileTeamId, channel.Id, ri.CreatorId, ri.Id)
|
|
if tc.uploadAsBookmark {
|
|
expectedDir = fmt.Sprintf("%v/teams/%v/channels/%v/%s", model.BookmarkFileOwner, FileTeamId, channel.Id, ri.Id)
|
|
}
|
|
expectedPath := fmt.Sprintf("%s/%s", expectedDir, fname)
|
|
assert.Equal(t, dbInfo.Path, expectedPath,
|
|
fmt.Sprintf("File %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.Path, expectedPath))
|
|
|
|
if tc.expectImage {
|
|
// We convert all other image types to jpeg, except pngs.
|
|
if ext != ".png" {
|
|
ext = ".jpg"
|
|
}
|
|
expectedThumbnailPath := fmt.Sprintf("%s/%s_thumb%s", expectedDir, name, ext)
|
|
expectedPreviewPath := fmt.Sprintf("%s/%s_preview%s", expectedDir, name, ext)
|
|
assert.Equal(t, dbInfo.ThumbnailPath, expectedThumbnailPath,
|
|
fmt.Sprintf("Thumbnail for %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.ThumbnailPath, expectedThumbnailPath))
|
|
assert.Equal(t, dbInfo.PreviewPath, expectedPreviewPath,
|
|
fmt.Sprintf("Preview for %v saved to:%q, expected:%q", dbInfo.Name, dbInfo.PreviewPath, expectedPreviewPath))
|
|
|
|
assert.True(t,
|
|
dbInfo.HasPreviewImage == tc.expectedImageHasPreview[i],
|
|
fmt.Sprintf("Image: HasPreviewImage should be set for %s", dbInfo.Name))
|
|
assert.True(t,
|
|
dbInfo.Width == tc.expectedImageWidths[i] && dbInfo.Height == tc.expectedImageHeights[i],
|
|
fmt.Sprintf("Image dimensions: expected %dwx%dh, got %vwx%dh",
|
|
tc.expectedImageWidths[i], tc.expectedImageHeights[i],
|
|
dbInfo.Width, dbInfo.Height))
|
|
}
|
|
|
|
if !tc.skipPayloadValidation {
|
|
compare := func(get func(context.Context, string) ([]byte, *model.Response, error), name string) {
|
|
var data []byte
|
|
data, _, err = get(context.Background(), ri.Id)
|
|
require.NoError(t, err)
|
|
|
|
var expected []byte
|
|
expected, err = os.ReadFile(filepath.Join(testDir, name))
|
|
require.NoError(t, err)
|
|
if !bytes.Equal(data, expected) {
|
|
var tf *os.File
|
|
tf, err = os.CreateTemp("", fmt.Sprintf("test_%v_*_%s", i, name))
|
|
require.NoError(t, err)
|
|
defer tf.Close()
|
|
_, err = io.Copy(tf, bytes.NewReader(data))
|
|
require.NoError(t, err)
|
|
t.Errorf("Actual data mismatched %s, written to %q - expected %d bytes, got %d.", name, tf.Name(), len(expected), len(data))
|
|
}
|
|
}
|
|
if len(tc.expectedPayloadNames) == 0 {
|
|
tc.expectedPayloadNames = tc.names
|
|
}
|
|
|
|
compare(client.GetFile, tc.expectedPayloadNames[i])
|
|
if len(tc.expectedImageThumbnailNames) > i {
|
|
compare(client.GetFileThumbnail, tc.expectedImageThumbnailNames[i])
|
|
}
|
|
if len(tc.expectedImageThumbnailNames) > i {
|
|
compare(client.GetFilePreview, tc.expectedImagePreviewNames[i])
|
|
}
|
|
}
|
|
|
|
err = th.cleanupTestFile(dbInfo)
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetFile(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
data, _, err := client.GetFile(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, len(data), "should not be empty")
|
|
|
|
for i := range data {
|
|
require.Equal(t, sent[i], data[i], "received file didn't match sent one")
|
|
}
|
|
|
|
_, resp, err := client.GetFile(context.Background(), "junk")
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.GetFile(context.Background(), model.NewId())
|
|
require.Error(t, err)
|
|
CheckNotFoundStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFile(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
})
|
|
|
|
t.Run("content reviewer should be able to get file of channel and team they are not a member of", func(t *testing.T) {
|
|
th.LoginBasic(t)
|
|
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
require.True(t, ok, "failed to set license")
|
|
|
|
defer func() {
|
|
appErr := th.App.Srv().RemoveLicense()
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
newChannel := th.CreatePrivateChannel(t)
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
|
|
post := th.CreatePostWithFilesWithClient(t, client, newChannel, fileResp.FileInfos[0])
|
|
|
|
reviewer := th.CreateUser(t)
|
|
response, err := th.SystemAdminClient.SaveContentFlaggingSettings(context.Background(), &model.ContentFlaggingSettingsRequest{
|
|
ContentFlaggingSettingsBase: model.ContentFlaggingSettingsBase{
|
|
EnableContentFlagging: model.NewPointer(true),
|
|
},
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{reviewer.Id},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
CheckOKStatus(t, response)
|
|
|
|
response, err = client.FlagPostForContentReview(context.Background(), post.Id, &model.FlagContentRequest{
|
|
Reason: "Classification mismatch",
|
|
Comment: "This is sensitive content",
|
|
})
|
|
require.NoError(t, err)
|
|
CheckOKStatus(t, response)
|
|
|
|
reviewerClient := th.CreateClient()
|
|
_, response, err = reviewerClient.Login(context.Background(), reviewer.Email, reviewer.Password)
|
|
require.NoError(t, err)
|
|
CheckOKStatus(t, response)
|
|
|
|
_, response, err = reviewerClient.GetFileAsContentReviewer(context.Background(), fileResp.FileInfos[0].Id, post.Id)
|
|
require.NoError(t, err)
|
|
CheckOKStatus(t, response)
|
|
|
|
// Try again after removing the user from content reviewers
|
|
response, err = th.SystemAdminClient.SaveContentFlaggingSettings(context.Background(), &model.ContentFlaggingSettingsRequest{
|
|
ContentFlaggingSettingsBase: model.ContentFlaggingSettingsBase{
|
|
EnableContentFlagging: model.NewPointer(true),
|
|
},
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
CheckOKStatus(t, response)
|
|
|
|
_, response, err = reviewerClient.GetFileAsContentReviewer(context.Background(), fileResp.FileInfos[0].Id, post.Id)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, response)
|
|
})
|
|
}
|
|
|
|
func TestGetFileAsSystemAdmin(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("public channel without membership", func(t *testing.T) {
|
|
publicChannel := th.CreateChannelWithClient(t, th.Client, model.ChannelTypeOpen)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, publicChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("public channel with membership", func(t *testing.T) {
|
|
publicChannel := th.CreatePublicChannel(t)
|
|
th.LinkUserToTeam(t, th.SystemAdminUser, th.BasicTeam)
|
|
th.AddUserToChannel(t, th.SystemAdminUser, publicChannel)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, publicChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("private channel without membership", func(t *testing.T) {
|
|
privateChannel := th.CreatePrivateChannel(t)
|
|
privateFileResp, _, err := th.Client.UploadFile(context.Background(), sent, privateChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), privateFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("private channel with membership", func(t *testing.T) {
|
|
privateChannel := th.CreatePrivateChannel(t)
|
|
th.LinkUserToTeam(t, th.SystemAdminUser, th.BasicTeam)
|
|
th.AddUserToChannel(t, th.SystemAdminUser, privateChannel)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, privateChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("direct message without membership", func(t *testing.T) {
|
|
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
|
|
dmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, dmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), dmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("direct message with membership", func(t *testing.T) {
|
|
dmChannel, _, err := th.SystemAdminClient.CreateDirectChannel(context.Background(), th.SystemAdminUser.Id, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, dmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("group message without membership", func(t *testing.T) {
|
|
user3 := th.CreateUser(t)
|
|
gmChannel, _, err := th.Client.CreateGroupChannel(context.Background(), []string{th.BasicUser.Id, th.BasicUser2.Id, user3.Id})
|
|
require.NoError(t, err)
|
|
gmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, gmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), gmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("group message with membership", func(t *testing.T) {
|
|
user3 := th.CreateUser(t)
|
|
gmChannel, _, err := th.SystemAdminClient.CreateGroupChannel(context.Background(), []string{th.SystemAdminUser.Id, th.BasicUser.Id, user3.Id})
|
|
require.NoError(t, err)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, gmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFile(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGetFileHeaders(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
CheckStartsWith := func(tb testing.TB, value, prefix, message string) {
|
|
tb.Helper()
|
|
|
|
require.True(tb, strings.HasPrefix(value, prefix), fmt.Sprintf("%s: %s", message, value))
|
|
}
|
|
|
|
testHeaders := func(data []byte, filename string, expectedContentType string, getInline bool, loadFile bool) func(*testing.T) {
|
|
return func(t *testing.T) {
|
|
if loadFile {
|
|
var err error
|
|
data, err = testutils.ReadTestFile(filename)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), data, channel.Id, filename)
|
|
require.NoError(t, err)
|
|
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
_, resp, err := client.GetFile(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
|
|
CheckStartsWith(t, resp.Header.Get("Content-Type"), expectedContentType, "returned incorrect Content-Type")
|
|
|
|
if getInline {
|
|
CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "inline", "returned incorrect Content-Disposition")
|
|
} else {
|
|
CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "attachment", "returned incorrect Content-Disposition")
|
|
}
|
|
|
|
_, resp, err = client.DownloadFile(context.Background(), fileId, true)
|
|
require.NoError(t, err)
|
|
|
|
CheckStartsWith(t, resp.Header.Get("Content-Type"), expectedContentType, "returned incorrect Content-Type")
|
|
CheckStartsWith(t, resp.Header.Get("Content-Disposition"), "attachment", "returned incorrect Content-Disposition")
|
|
}
|
|
}
|
|
|
|
data := []byte("ABC")
|
|
|
|
t.Run("png", testHeaders(data, "test.png", "image/png", true, true))
|
|
t.Run("gif", testHeaders(data, "testgif.gif", "image/gif", true, true))
|
|
t.Run("mp4", testHeaders(data, "test.mp4", "video/mp4", true, false))
|
|
t.Run("mp3", testHeaders(data, "test.mp3", "audio/mpeg", true, false))
|
|
t.Run("pdf", testHeaders(data, "test.pdf", "application/pdf", false, false))
|
|
t.Run("txt", testHeaders(data, "test.txt", "text/plain", false, false))
|
|
t.Run("html", testHeaders(data, "test.html", "text/plain", false, false))
|
|
t.Run("js", testHeaders(data, "test.js", "text/plain", false, false))
|
|
// *.go are categorized differently by different platforms
|
|
// t.Run("go", testHeaders(data, "test.go", "text/x-go; charset=utf-8", false, false))
|
|
t.Run("zip", testHeaders(data, "test.zip", "application/zip", false, false))
|
|
// Not every platform can recognize these
|
|
// t.Run("exe", testHeaders(data, "test.exe", "application/x-ms", false))
|
|
t.Run("no extension", testHeaders(data, "test", "application/octet-stream", false, false))
|
|
t.Run("no extension 2", testHeaders([]byte("<html></html>"), "test", "application/octet-stream", false, false))
|
|
}
|
|
|
|
func TestGetFileThumbnail(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
data, _, err := client.GetFileThumbnail(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, len(data), "should not be empty")
|
|
|
|
_, resp, err := client.GetFileThumbnail(context.Background(), "junk")
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.GetFileThumbnail(context.Background(), model.NewId())
|
|
require.Error(t, err)
|
|
CheckNotFoundStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileThumbnail(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
|
|
otherUser := th.CreateUser(t)
|
|
_, _, err = client.Login(context.Background(), otherUser.Email, otherUser.Password)
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileThumbnail(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
}
|
|
|
|
func TestGetFileThumbnailAsSystemAdmin(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("public channel without membership", func(t *testing.T) {
|
|
th.LinkUserToTeam(t, th.BasicUser, th.BasicTeam)
|
|
publicChannel := th.CreateChannelWithClient(t, th.Client, model.ChannelTypeOpen)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, publicChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("public channel with membership", func(t *testing.T) {
|
|
th.LinkUserToTeam(t, th.SystemAdminUser, th.BasicTeam)
|
|
publicChannel := th.CreateChannelWithClient(t, th.Client, model.ChannelTypeOpen)
|
|
th.AddUserToChannel(t, th.SystemAdminUser, publicChannel)
|
|
fileResp, _, err := th.Client.UploadFile(context.Background(), sent, publicChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), fileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("private channel without membership", func(t *testing.T) {
|
|
th.LinkUserToTeam(t, th.SystemAdminUser, th.BasicTeam)
|
|
privateChannel := th.CreatePrivateChannel(t)
|
|
privateFileResp, _, err := th.Client.UploadFile(context.Background(), sent, privateChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), privateFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("private channel with membership", func(t *testing.T) {
|
|
th.LinkUserToTeam(t, th.SystemAdminUser, th.BasicTeam)
|
|
privateChannel := th.CreatePrivateChannel(t)
|
|
th.AddUserToChannel(t, th.SystemAdminUser, privateChannel)
|
|
privateFileResp, _, err := th.Client.UploadFile(context.Background(), sent, privateChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), privateFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("direct message without membership", func(t *testing.T) {
|
|
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
|
|
dmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, dmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), dmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("direct message with membership", func(t *testing.T) {
|
|
dmChannel, _, err := th.SystemAdminClient.CreateDirectChannel(context.Background(), th.SystemAdminUser.Id, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
dmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, dmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), dmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("group message without membership", func(t *testing.T) {
|
|
user3 := th.CreateUser(t)
|
|
gmChannel, _, err := th.Client.CreateGroupChannel(context.Background(), []string{th.BasicUser.Id, th.BasicUser2.Id, user3.Id})
|
|
require.NoError(t, err)
|
|
gmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, gmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), gmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("group message with membership", func(t *testing.T) {
|
|
user3 := th.CreateUser(t)
|
|
gmChannel, _, err := th.SystemAdminClient.CreateGroupChannel(context.Background(), []string{th.SystemAdminUser.Id, th.BasicUser.Id, user3.Id})
|
|
require.NoError(t, err)
|
|
gmFileResp, _, err := th.Client.UploadFile(context.Background(), sent, gmChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileThumbnail(context.Background(), gmFileResp.FileInfos[0].Id)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGetFileLink(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
|
|
|
|
data, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), data, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
_, resp, err := client.GetFileLink(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileId, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = false })
|
|
_, resp, err = client.GetFileLink(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
|
|
link, _, err := client.GetFileLink(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, "", link, "should've received public link")
|
|
|
|
_, resp, err = client.GetFileLink(context.Background(), "junk")
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.GetFileLink(context.Background(), model.NewId())
|
|
require.Error(t, err)
|
|
CheckNotFoundStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileLink(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
|
|
otherUser := th.CreateUser(t)
|
|
_, _, err = client.Login(context.Background(), otherUser.Email, otherUser.Password)
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileLink(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileLink(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
|
|
fileInfo, err := th.App.Srv().Store().FileInfo().Get(fileId)
|
|
require.NoError(t, err)
|
|
err = th.cleanupTestFile(fileInfo)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestGetFilePreview(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
data, _, err := client.GetFilePreview(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, len(data), "should not be empty")
|
|
|
|
_, resp, err := client.GetFilePreview(context.Background(), "junk")
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.GetFilePreview(context.Background(), model.NewId())
|
|
require.Error(t, err)
|
|
CheckNotFoundStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFilePreview(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
|
|
otherUser := th.CreateUser(t)
|
|
_, _, err = client.Login(context.Background(), otherUser.Email, otherUser.Password)
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFilePreview(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFilePreview(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
}
|
|
|
|
func TestGetFileInfo(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
user := th.BasicUser
|
|
channel := th.BasicChannel
|
|
|
|
if *th.App.Config().FileSettings.DriverName == "" {
|
|
t.Skip("skipping because no file driver is enabled")
|
|
}
|
|
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
info, _, err := client.GetFileInfo(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, fileId, info.Id, "got incorrect file")
|
|
require.Equal(t, user.Id, info.CreatorId, "file should be assigned to user")
|
|
require.Equal(t, "", info.PostId, "file shouldn't have a post")
|
|
require.Equal(t, "", info.Path, "file path shouldn't have been returned to client")
|
|
require.Equal(t, "", info.ThumbnailPath, "file thumbnail path shouldn't have been returned to client")
|
|
require.Equal(t, "", info.PreviewPath, "file preview path shouldn't have been returned to client")
|
|
require.Equal(t, "image/png", info.MimeType, "mime type should've been image/png")
|
|
|
|
_, resp, err := client.GetFileInfo(context.Background(), "junk")
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.GetFileInfo(context.Background(), model.NewId())
|
|
require.Error(t, err)
|
|
CheckNotFoundStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileInfo(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
|
|
otherUser := th.CreateUser(t)
|
|
_, _, err = client.Login(context.Background(), otherUser.Email, otherUser.Password)
|
|
require.NoError(t, err)
|
|
_, resp, err = client.GetFileInfo(context.Background(), fileId)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, _, err = th.SystemAdminClient.GetFileInfo(context.Background(), fileId)
|
|
require.NoError(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
}
|
|
|
|
func TestGetPublicFile(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
channel := th.BasicChannel
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
|
|
|
|
data, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), data, channel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileId, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
info, err := th.App.Srv().Store().FileInfo().Get(fileId)
|
|
require.NoError(t, err)
|
|
link := th.App.GeneratePublicLink(client.URL, info)
|
|
|
|
resp, err := http.Get(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode, "failed to get image with public link")
|
|
|
|
resp, err = http.Head(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode, "failed to respond to HEAD request")
|
|
|
|
resp, err = http.Get(link[:strings.LastIndex(link, "?")])
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link without hash", resp.Status)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = false })
|
|
|
|
resp, err = http.Get(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusForbidden, resp.StatusCode, "should've failed to get image with disabled public link")
|
|
|
|
// test after the salt has changed
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnablePublicLink = true })
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewRandomString(32) })
|
|
|
|
resp, err = http.Get(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link after salt changed")
|
|
|
|
resp, err = http.Get(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode, "should've failed to get image with public link after salt changed")
|
|
|
|
err = th.cleanupTestFile(info)
|
|
require.NoError(t, err)
|
|
link = th.App.GeneratePublicLink(client.URL, info)
|
|
resp, err = http.Get(link)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusNotFound, resp.StatusCode, "should've failed to get file after it is deleted")
|
|
}
|
|
|
|
func TestSearchFilesInTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
data, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
th.LoginBasic(t)
|
|
client := th.Client
|
|
|
|
filename := "search for fileInfo1"
|
|
fileInfo1, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
|
|
require.Nil(t, appErr)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileInfo1.Id, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
filename = "search for fileInfo2"
|
|
fileInfo2, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
|
|
require.Nil(t, appErr)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileInfo2.Id, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
filename = "tagged search for fileInfo3"
|
|
fileInfo3, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
|
|
require.Nil(t, appErr)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileInfo3.Id, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
filename = "tagged for fileInfo4"
|
|
fileInfo4, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
|
|
require.Nil(t, appErr)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileInfo4.Id, th.BasicPost.Id, th.BasicPost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
|
|
archivedChannel := th.CreatePublicChannel(t)
|
|
fileInfo5, appErr := th.App.UploadFile(th.Context, data, archivedChannel.Id, "tagged for fileInfo3")
|
|
require.Nil(t, appErr)
|
|
post := &model.Post{ChannelId: archivedChannel.Id, Message: model.NewId() + "a"}
|
|
rpost, _, err := client.CreatePost(context.Background(), post)
|
|
require.NoError(t, err)
|
|
err = th.App.Srv().Store().FileInfo().AttachToPost(th.Context, fileInfo5.Id, rpost.Id, rpost.ChannelId, th.BasicUser.Id)
|
|
require.NoError(t, err)
|
|
_, err = th.Client.DeleteChannel(context.Background(), archivedChannel.Id)
|
|
require.NoError(t, err)
|
|
|
|
terms := "search"
|
|
isOrSearch := false
|
|
timezoneOffset := 5
|
|
searchParams := model.SearchParameter{
|
|
Terms: &terms,
|
|
IsOrSearch: &isOrSearch,
|
|
TimeZoneOffset: &timezoneOffset,
|
|
}
|
|
fileInfos, _, err := client.SearchFilesWithParams(context.Background(), th.BasicTeam.Id, &searchParams)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 3, "wrong search")
|
|
|
|
terms = "search"
|
|
page := 0
|
|
perPage := 2
|
|
searchParams = model.SearchParameter{
|
|
Terms: &terms,
|
|
IsOrSearch: &isOrSearch,
|
|
TimeZoneOffset: &timezoneOffset,
|
|
Page: &page,
|
|
PerPage: &perPage,
|
|
}
|
|
fileInfos2, _, err := client.SearchFilesWithParams(context.Background(), th.BasicTeam.Id, &searchParams)
|
|
require.NoError(t, err)
|
|
// We don't support paging for DB search yet, modify this when we do.
|
|
require.Len(t, fileInfos2.Order, 3, "Wrong number of fileInfos")
|
|
assert.Equal(t, fileInfos.Order[0], fileInfos2.Order[0])
|
|
assert.Equal(t, fileInfos.Order[1], fileInfos2.Order[1])
|
|
|
|
page = 1
|
|
searchParams = model.SearchParameter{
|
|
Terms: &terms,
|
|
IsOrSearch: &isOrSearch,
|
|
TimeZoneOffset: &timezoneOffset,
|
|
Page: &page,
|
|
PerPage: &perPage,
|
|
}
|
|
fileInfos2, _, err = client.SearchFilesWithParams(context.Background(), th.BasicTeam.Id, &searchParams)
|
|
require.NoError(t, err)
|
|
// We don't support paging for DB search yet, modify this when we do.
|
|
require.Empty(t, fileInfos2.Order, "Wrong number of fileInfos")
|
|
|
|
fileInfos, _, err = client.SearchFiles(context.Background(), th.BasicTeam.Id, "search", false)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 3, "wrong search")
|
|
|
|
fileInfos, _, err = client.SearchFiles(context.Background(), th.BasicTeam.Id, "fileInfo2", false)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 1, "wrong number of fileInfos")
|
|
require.Equal(t, fileInfo2.Id, fileInfos.Order[0], "wrong search")
|
|
|
|
terms = "tagged"
|
|
includeDeletedChannels := true
|
|
searchParams = model.SearchParameter{
|
|
Terms: &terms,
|
|
IsOrSearch: &isOrSearch,
|
|
TimeZoneOffset: &timezoneOffset,
|
|
IncludeDeletedChannels: &includeDeletedChannels,
|
|
}
|
|
fileInfos, _, err = client.SearchFilesWithParams(context.Background(), th.BasicTeam.Id, &searchParams)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 3, "wrong search")
|
|
|
|
// Archived channels are always included now, so this should return the same result
|
|
fileInfos, _, err = client.SearchFilesWithParams(context.Background(), th.BasicTeam.Id, &searchParams)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 3, "wrong search")
|
|
|
|
fileInfos, _, _ = client.SearchFiles(context.Background(), th.BasicTeam.Id, "*", false)
|
|
require.Empty(t, fileInfos.Order, "searching for just * shouldn't return any results")
|
|
|
|
fileInfos, _, err = client.SearchFiles(context.Background(), th.BasicTeam.Id, "fileInfo1 fileInfo2", true)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 2, "wrong search results")
|
|
|
|
_, resp, err := client.SearchFiles(context.Background(), "junk", "#sgtitlereview", false)
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, resp, err = client.SearchFiles(context.Background(), model.NewId(), "#sgtitlereview", false)
|
|
require.Error(t, err)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
_, resp, err = client.SearchFiles(context.Background(), th.BasicTeam.Id, "", false)
|
|
require.Error(t, err)
|
|
CheckBadRequestStatus(t, resp)
|
|
|
|
_, err = client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, resp, err = client.SearchFiles(context.Background(), th.BasicTeam.Id, "#sgtitlereview", false)
|
|
require.Error(t, err)
|
|
CheckUnauthorizedStatus(t, resp)
|
|
}
|
|
|
|
func TestSearchFilesAcrossTeams(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
data, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
th.LoginBasic(t)
|
|
client := th.Client
|
|
|
|
var teams [2]*model.Team
|
|
var channels [2]*model.Channel
|
|
for i := range 2 {
|
|
teams[i] = th.CreateTeam(t)
|
|
channels[i] = th.CreateChannelWithClientAndTeam(t, th.Client, model.ChannelTypeOpen, teams[i].Id)
|
|
|
|
th.LinkUserToTeam(t, th.BasicUser, teams[i])
|
|
th.AddUserToChannel(t, th.BasicUser, channels[i])
|
|
|
|
filename := "search for fileInfo"
|
|
fileInfo, appErr := th.App.UploadFile(th.Context, data, th.BasicChannel.Id, filename)
|
|
require.Nil(t, appErr)
|
|
|
|
th.CreatePostInChannelWithFiles(t, channels[i], fileInfo)
|
|
}
|
|
|
|
terms := "search"
|
|
|
|
// BasicUser should have access to all the files
|
|
fileInfos, _, err := client.SearchFilesAcrossTeams(context.Background(), terms, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 2, "wrong search")
|
|
|
|
// a new user that only belongs to the first team should only get one result
|
|
newUser := th.CreateUser(t)
|
|
th.LinkUserToTeam(t, newUser, teams[0])
|
|
th.AddUserToChannel(t, newUser, channels[0])
|
|
th.UnlinkUserFromTeam(t, th.BasicUser, teams[1])
|
|
|
|
_, err = th.Client.Logout(context.Background())
|
|
require.NoError(t, err)
|
|
_, _, err = th.Client.Login(context.Background(), newUser.Email, newUser.Password)
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, _, err = client.SearchFilesAcrossTeams(context.Background(), terms, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos.Order, 1, "wrong search")
|
|
require.Equal(t, fileInfos.FileInfos[fileInfos.Order[0]].ChannelId, channels[0].Id, "wrong search")
|
|
}
|
|
|
|
// TestHeadRequestsFileEndpoints tests that HEAD requests work correctly for file endpoints
|
|
func TestHeadRequestsFileEndpoints(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
client := th.Client
|
|
|
|
// Upload a test file
|
|
sent, err := testutils.ReadTestFile("test.png")
|
|
require.NoError(t, err)
|
|
|
|
fileResp, _, err := client.UploadFile(context.Background(), sent, th.BasicChannel.Id, "test.png")
|
|
require.NoError(t, err)
|
|
fileId := fileResp.FileInfos[0].Id
|
|
|
|
// Helper function to make HEAD requests
|
|
makeHeadRequest := func(url string) (*http.Response, error) {
|
|
req, err := http.NewRequest("HEAD", url, nil)
|
|
require.NoError(t, err)
|
|
|
|
req.Header.Set(model.HeaderAuth, client.AuthType+" "+client.AuthToken)
|
|
|
|
return client.HTTPClient.Do(req)
|
|
}
|
|
|
|
t.Run("HEAD request to file endpoint returns 200 OK", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s", client.APIURL, fileId)
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
require.NotEmpty(t, resp.Header.Get("Content-Type"))
|
|
})
|
|
|
|
t.Run("HEAD request to thumbnail endpoint returns 200 OK", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s/thumbnail", client.APIURL, fileId)
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("HEAD request to preview endpoint returns 200 OK", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s/preview", client.APIURL, fileId)
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("HEAD request returns no body", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s", client.APIURL, fileId)
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Empty(t, body, "HEAD response should not contain a body")
|
|
})
|
|
|
|
t.Run("HEAD request requires authentication", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s", client.APIURL, fileId)
|
|
req, err := http.NewRequest("HEAD", url, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Don't set auth header
|
|
resp, err := client.HTTPClient.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("HEAD request with invalid file ID returns 400", func(t *testing.T) {
|
|
// Use an ID that matches the route pattern [A-Za-z0-9]+ but is invalid (not 26 characters)
|
|
url := fmt.Sprintf("%s/files/%s", client.APIURL, "invalidid")
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("HEAD request for non-existent file returns 404", func(t *testing.T) {
|
|
url := fmt.Sprintf("%s/files/%s", client.APIURL, model.NewId())
|
|
resp, err := makeHeadRequest(url)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
|
})
|
|
}
|