Merge branch 'master' of github.com:mattermost/mattermost-server into top-dms-clean
Этот коммит содержится в:
@@ -322,8 +322,8 @@ func SetupWithStoreMock(tb testing.TB) *TestHelper {
|
||||
return th
|
||||
}
|
||||
|
||||
func SetupEnterpriseWithStoreMock(tb testing.TB) *TestHelper {
|
||||
th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, true, false, nil, nil)
|
||||
func SetupEnterpriseWithStoreMock(tb testing.TB, options ...app.Option) *TestHelper {
|
||||
th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, true, false, nil, options)
|
||||
statusMock := mocks.StatusStore{}
|
||||
statusMock.On("UpdateExpiredDNDStatuses").Return([]*model.Status{}, nil)
|
||||
statusMock.On("Get", "user1").Return(&model.Status{UserId: "user1", Status: model.StatusOnline}, nil)
|
||||
@@ -381,7 +381,7 @@ func (th *TestHelper) TearDown() {
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r.Body != nil {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_, _ = io.Copy(io.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ package api4
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/audit"
|
||||
@@ -33,7 +32,7 @@ func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
defer io.Copy(io.Discard, r.Body)
|
||||
|
||||
if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize {
|
||||
c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge)
|
||||
|
||||
@@ -6,7 +6,6 @@ package api4
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app"
|
||||
@@ -32,7 +31,7 @@ func (api *API) InitEmoji() {
|
||||
}
|
||||
|
||||
func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
defer io.Copy(io.Discard, r.Body)
|
||||
|
||||
if !*c.App.Config().ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
|
||||
46
api4/post.go
46
api4/post.go
@@ -34,6 +34,8 @@ func (api *API) InitPost() {
|
||||
api.BaseRoutes.Post.Handle("", api.APISessionRequired(updatePost)).Methods("PUT")
|
||||
api.BaseRoutes.Post.Handle("/patch", api.APISessionRequired(patchPost)).Methods("PUT")
|
||||
api.BaseRoutes.PostForUser.Handle("/set_unread", api.APISessionRequired(setPostUnread)).Methods("POST")
|
||||
api.BaseRoutes.PostForUser.Handle("/reminder", api.APISessionRequired(setPostReminder)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.Post.Handle("/pin", api.APISessionRequired(pinPost)).Methods("POST")
|
||||
api.BaseRoutes.Post.Handle("/unpin", api.APISessionRequired(unpinPost)).Methods("POST")
|
||||
}
|
||||
@@ -401,7 +403,7 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Post is inaccessible due to cloud plan's limit.
|
||||
if err.Id == "app.post.cloud.get.app_error" {
|
||||
w.Header().Set(model.HeaderHasInaccessiblePosts, "true")
|
||||
w.Header().Set(model.HeaderFirstInaccessiblePostTime, "1")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -438,7 +440,7 @@ func getPostsByIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
postsList, hasInaccessiblePosts, err := c.App.GetPostsByIds(postIDs)
|
||||
postsList, firstInaccessiblePostTime, err := c.App.GetPostsByIds(postIDs)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -471,7 +473,7 @@ func getPostsByIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
posts = append(posts, post)
|
||||
}
|
||||
|
||||
w.Header().Set(model.HeaderHasInaccessiblePosts, strconv.FormatBool(hasInaccessiblePosts))
|
||||
w.Header().Set(model.HeaderFirstInaccessiblePostTime, strconv.FormatInt(firstInaccessiblePostTime, 10))
|
||||
|
||||
if err := json.NewEncoder(w).Encode(posts); err != nil {
|
||||
mlog.Warn("Error while writing response", mlog.Err(err))
|
||||
@@ -842,6 +844,36 @@ func setPostUnread(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func setPostReminder(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequirePostId().RequireUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.AppContext.Session().UserId != c.Params.UserId && !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), c.Params.UserId) {
|
||||
c.SetPermissionError(model.PermissionEditOtherUsers)
|
||||
return
|
||||
}
|
||||
if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannel) {
|
||||
c.SetPermissionError(model.PermissionReadChannel)
|
||||
return
|
||||
}
|
||||
|
||||
var reminder model.PostReminder
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&reminder); jsonErr != nil {
|
||||
c.SetInvalidParam("target_time")
|
||||
return
|
||||
}
|
||||
|
||||
appErr := c.App.SetPostReminder(c.Params.PostId, c.Params.UserId, reminder.TargetTime)
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func saveIsPinnedPost(c *Context, w http.ResponseWriter, isPinned bool) {
|
||||
c.RequirePostId()
|
||||
if c.Err != nil {
|
||||
@@ -898,7 +930,13 @@ func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId)
|
||||
includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted"))
|
||||
if includeDeleted && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
||||
c.SetPermissionError(model.PermissionManageSystem)
|
||||
return
|
||||
}
|
||||
|
||||
infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId, includeDeleted)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
||||
@@ -2623,6 +2623,39 @@ func TestGetFileInfosForPost(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// Delete post
|
||||
th.SystemAdminClient.DeletePost(post.Id)
|
||||
|
||||
// Normal client should get 404 when trying to access deleted post normally
|
||||
_, resp, err = client.GetFileInfosForPost(post.Id, "")
|
||||
require.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
// Normal client should get unauthorized when trying to access deleted post
|
||||
_, resp, err = client.GetFileInfosForPostIncludeDeleted(post.Id, "")
|
||||
require.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// System client should get 404 when trying to access deleted post normally
|
||||
_, resp, err = th.SystemAdminClient.GetFileInfosForPost(post.Id, "")
|
||||
require.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
// System client should be able to access deleted post with include_deleted param
|
||||
infos, _, err = th.SystemAdminClient.GetFileInfosForPostIncludeDeleted(post.Id, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, infos, 3, "missing file infos")
|
||||
|
||||
found = false
|
||||
for _, info := range infos {
|
||||
if info.Id == fileIds[0] {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
require.True(t, found, "missing file info")
|
||||
|
||||
client.Logout()
|
||||
_, resp, err = client.GetFileInfosForPost(model.NewId(), "")
|
||||
require.Error(t, err)
|
||||
@@ -3131,3 +3164,63 @@ func TestGetPostStripActionIntegrations(t *testing.T) {
|
||||
// integration must be omitted
|
||||
require.Nil(t, action["integration"])
|
||||
}
|
||||
|
||||
func TestPostReminder(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
client := th.Client
|
||||
userWSClient, err := th.CreateWebSocketClient()
|
||||
require.NoError(t, err)
|
||||
defer userWSClient.Close()
|
||||
userWSClient.Listen()
|
||||
|
||||
targetTime := time.Now().UTC().Unix()
|
||||
resp, err := client.SetPostReminder(&model.PostReminder{
|
||||
TargetTime: targetTime,
|
||||
PostId: th.BasicPost.Id,
|
||||
UserId: th.BasicUser.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
post, _, err := client.GetPost(th.BasicPost.Id, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
user, _, err := client.GetUser(post.UserId, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
var caught bool
|
||||
func() {
|
||||
for {
|
||||
select {
|
||||
case ev := <-userWSClient.EventChannel:
|
||||
if ev.EventType() == model.WebsocketEventEphemeralMessage {
|
||||
caught = true
|
||||
data := ev.GetData()
|
||||
|
||||
post, ok := data["post"].(string)
|
||||
require.True(t, ok)
|
||||
|
||||
var parsedPost model.Post
|
||||
err := json.Unmarshal([]byte(post), &parsedPost)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, model.PostTypeEphemeral, parsedPost.Type)
|
||||
assert.Equal(t, th.BasicUser.Id, parsedPost.UserId)
|
||||
assert.Equal(t, th.BasicPost.Id, parsedPost.RootId)
|
||||
|
||||
require.Equal(t, float64(targetTime), parsedPost.GetProp("target_time").(float64))
|
||||
require.Equal(t, th.BasicPost.Id, parsedPost.GetProp("post_id").(string))
|
||||
require.Equal(t, user.Username, parsedPost.GetProp("username").(string))
|
||||
require.Equal(t, th.BasicTeam.Name, parsedPost.GetProp("team_name").(string))
|
||||
return
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
require.Truef(t, caught, "User should have received %s event", model.WebsocketEventEphemeralMessage)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package api4
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -222,7 +221,7 @@ func uploadRemoteData(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func remoteSetProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
defer io.Copy(io.Discard, r.Body)
|
||||
|
||||
c.RequireUserId()
|
||||
if c.Err != nil {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"reflect"
|
||||
@@ -527,7 +526,7 @@ func getRedirectLocation(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, res.Body)
|
||||
io.Copy(io.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
}()
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -1610,7 +1609,7 @@ func getTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func setTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
defer io.Copy(io.Discard, r.Body)
|
||||
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
|
||||
@@ -207,7 +207,7 @@ func TestUploadData(t *testing.T) {
|
||||
CreateAt: model.GetMillis(),
|
||||
UserId: th.BasicUser2.Id,
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
Filename: "upload",
|
||||
Filename: "upload.zip",
|
||||
FileSize: 8 * 1024 * 1024,
|
||||
}
|
||||
us, err := th.App.CreateUploadSession(th.Context, us)
|
||||
@@ -281,6 +281,9 @@ func TestUploadData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, info)
|
||||
require.Equal(t, u.Filename, info.Name)
|
||||
require.Equal(t, u.FileSize, info.Size)
|
||||
require.Equal(t, "zip", info.Extension)
|
||||
require.Equal(t, "application/zip", info.MimeType)
|
||||
|
||||
file, _, err := th.Client.GetFile(info.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -419,7 +418,7 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
defer io.Copy(io.Discard, r.Body)
|
||||
|
||||
c.RequireUserId()
|
||||
if c.Err != nil {
|
||||
@@ -1881,11 +1880,6 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||
}
|
||||
|
||||
// For context see: https://mattermost.atlassian.net/browse/MM-39583
|
||||
if c.App.Channels().License() != nil && *c.App.Channels().License().Features.Cloud {
|
||||
c.App.AttachCloudSessionCookie(c.AppContext, w, r)
|
||||
}
|
||||
|
||||
userTermsOfService, err := c.App.GetUserTermsOfService(user.Id)
|
||||
if err != nil && err.StatusCode != http.StatusNotFound {
|
||||
c.Err = err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -3610,14 +3611,61 @@ func TestLoginCookies(t *testing.T) {
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
th.Client.HTTPHeader[model.HeaderRequestedWith] = model.HeaderRequestedWithXML
|
||||
_, resp, _ := th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
|
||||
val := strings.Split(resp.Header["Set-Cookie"][0], ";")
|
||||
cloudSessionCookie := strings.Split(val[0], "=")[1]
|
||||
domain := strings.Split(val[2], "=")[1]
|
||||
found := false
|
||||
cookies := resp.Header.Values("Set-Cookie")
|
||||
for i := range cookies {
|
||||
if strings.Contains(cookies[i], "MMCLOUDURL") {
|
||||
found = true
|
||||
assert.Contains(t, cookies[i], "MMCLOUDURL=testchips;", "should contain MMCLOUDURL")
|
||||
assert.Contains(t, cookies[i], "Domain=mattermost.com;", "should contain Domain=mattermost.com")
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Did not find MMCLOUDURL cookie")
|
||||
})
|
||||
|
||||
assert.Equal(t, "testchips", cloudSessionCookie)
|
||||
assert.Equal(t, "mattermost.com", domain)
|
||||
t.Run("should return cookie with MMCLOUDURL for cloud installations when doing cws login", func(t *testing.T) {
|
||||
token := model.NewRandomString(64)
|
||||
os.Setenv("CWS_CLOUD_TOKEN", token)
|
||||
|
||||
updateConfig := func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.SiteURL = "https://testchips.cloud.mattermost.com"
|
||||
}
|
||||
th := SetupAndApplyConfigBeforeLogin(t, updateConfig).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("login_id", th.SystemAdminUser.Email)
|
||||
form.Add("cws_token", token)
|
||||
|
||||
th.Client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
r, _ := th.Client.DoAPIRequestWithHeaders(
|
||||
http.MethodPost,
|
||||
th.Client.APIURL+"/users/login/cws",
|
||||
form.Encode(),
|
||||
map[string]string{
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
)
|
||||
defer closeBody(r)
|
||||
|
||||
cookies := r.Cookies()
|
||||
found := false
|
||||
for i := range cookies {
|
||||
if cookies[i].Name == model.SessionCookieCloudUrl {
|
||||
found = true
|
||||
assert.Equal(t, "testchips", cookies[i].Value)
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "should have found cookie")
|
||||
})
|
||||
|
||||
t.Run("should NOT return cookie with MMCLOUDURL for cloud installations without expected format of cloud URL", func(t *testing.T) {
|
||||
|
||||
Ссылка в новой задаче
Block a user