diff options
Diffstat (limited to 'api4')
-rw-r--r-- | api4/apitestlib.go | 27 | ||||
-rw-r--r-- | api4/channel_test.go | 7 | ||||
-rw-r--r-- | api4/system.go | 41 | ||||
-rw-r--r-- | api4/system_test.go | 64 | ||||
-rw-r--r-- | api4/team.go | 83 | ||||
-rw-r--r-- | api4/team_test.go | 81 | ||||
-rw-r--r-- | api4/webhook.go | 1 |
7 files changed, 263 insertions, 41 deletions
diff --git a/api4/apitestlib.go b/api4/apitestlib.go index e55ca8c8b..2bb80ddfb 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -510,18 +510,6 @@ func CheckUserSanitization(t *testing.T, user *model.User) { } } -func CheckTeamSanitization(t *testing.T, team *model.Team) { - t.Helper() - - if team.Email != "" { - t.Fatal("email wasn't blank") - } - - if team.AllowedDomains != "" { - t.Fatal("'allowed domains' wasn't blank") - } -} - func CheckEtag(t *testing.T, data interface{}, resp *model.Response) { t.Helper() @@ -669,21 +657,6 @@ func CheckInternalErrorStatus(t *testing.T, resp *model.Response) { } } -func CheckPayLoadTooLargeStatus(t *testing.T, resp *model.Response) { - t.Helper() - - if resp.Error == nil { - t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusRequestEntityTooLarge)) - return - } - - if resp.StatusCode != http.StatusRequestEntityTooLarge { - t.Log("actual: " + strconv.Itoa(resp.StatusCode)) - t.Log("expected: " + strconv.Itoa(http.StatusRequestEntityTooLarge)) - t.Fatal("wrong status code") - } -} - func readTestFile(name string) ([]byte, error) { path, _ := utils.FindDir("tests") file, err := os.Open(path + "/" + name) diff --git a/api4/channel_test.go b/api4/channel_test.go index e65918707..51c32cf71 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -13,7 +13,6 @@ import ( "testing" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store/sqlstore" ) func TestCreateChannel(t *testing.T) { @@ -909,7 +908,7 @@ func TestDeleteChannel(t *testing.T) { // successful delete by channel admin th.MakeUserChannelAdmin(user, publicChannel6) th.MakeUserChannelAdmin(user, privateChannel7) - sqlstore.ClearChannelCaches() + th.App.Srv.Store.Channel().ClearCaches() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckNoError(t, resp) @@ -960,7 +959,7 @@ func TestDeleteChannel(t *testing.T) { // // cannot delete by channel admin th.MakeUserChannelAdmin(user, publicChannel6) th.MakeUserChannelAdmin(user, privateChannel7) - sqlstore.ClearChannelCaches() + th.App.Srv.Store.Channel().ClearCaches() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckForbiddenStatus(t, resp) @@ -1001,7 +1000,7 @@ func TestDeleteChannel(t *testing.T) { // cannot delete by channel admin th.MakeUserChannelAdmin(user, publicChannel6) th.MakeUserChannelAdmin(user, privateChannel7) - sqlstore.ClearChannelCaches() + th.App.Srv.Store.Channel().ClearCaches() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckForbiddenStatus(t, resp) diff --git a/api4/system.go b/api4/system.go index 2355cb476..c1541f0b5 100644 --- a/api4/system.go +++ b/api4/system.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "runtime" - "strconv" l4g "github.com/alecthomas/log4go" "github.com/mattermost/mattermost-server/model" @@ -29,6 +28,7 @@ func (api *API) InitSystem() { api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET") api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST") + api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST") api.BaseRoutes.ApiRoot.Handle("/database/recycle", api.ApiSessionRequired(databaseRecycle)).Methods("POST") api.BaseRoutes.ApiRoot.Handle("/caches/invalidate", api.ApiSessionRequired(invalidateCaches)).Methods("POST") @@ -246,14 +246,7 @@ func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) { return } - respCfg := map[string]string{} - for k, v := range c.App.ClientConfig() { - respCfg[k] = v - } - - respCfg["NoAccounts"] = strconv.FormatBool(c.App.IsFirstUserAccount()) - - w.Write([]byte(model.MapToJson(respCfg))) + w.Write([]byte(model.MapToJson(c.App.ClientConfigWithNoAccounts()))) } func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) { @@ -384,3 +377,33 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(rows.ToJson())) } + +func testS3(c *Context, w http.ResponseWriter, r *http.Request) { + cfg := model.ConfigFromJson(r.Body) + if cfg == nil { + cfg = c.App.Config() + } + + if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + err := utils.CheckMandatoryS3Fields(&cfg.FileSettings) + if err != nil { + c.Err = err + return + } + + license := c.App.License() + backend, appErr := utils.NewFileBackend(&cfg.FileSettings, license != nil && *license.Features.Compliance) + if appErr == nil { + appErr = backend.TestConnection() + } + if appErr != nil { + c.Err = appErr + return + } + + ReturnStatusOK(w) +} diff --git a/api4/system_test.go b/api4/system_test.go index 01b4934ae..e39486b77 100644 --- a/api4/system_test.go +++ b/api4/system_test.go @@ -1,7 +1,9 @@ package api4 import ( + "fmt" "net/http" + "os" "strings" "testing" @@ -466,3 +468,65 @@ func TestGetAnalyticsOld(t *testing.T) { _, resp = Client.GetAnalyticsOld("", th.BasicTeam.Id) CheckUnauthorizedStatus(t, resp) } + +func TestS3TestConnection(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + Client := th.Client + + s3Host := os.Getenv("CI_HOST") + if s3Host == "" { + s3Host = "dockerhost" + } + + s3Port := os.Getenv("CI_MINIO_PORT") + if s3Port == "" { + s3Port = "9001" + } + + s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port) + config := model.Config{ + FileSettings: model.FileSettings{ + DriverName: model.NewString(model.IMAGE_DRIVER_S3), + AmazonS3AccessKeyId: model.MINIO_ACCESS_KEY, + AmazonS3SecretAccessKey: model.MINIO_SECRET_KEY, + AmazonS3Bucket: "", + AmazonS3Endpoint: "", + AmazonS3SSL: model.NewBool(false), + }, + } + + _, resp := Client.TestS3Connection(&config) + CheckForbiddenStatus(t, resp) + + _, resp = th.SystemAdminClient.TestS3Connection(&config) + CheckBadRequestStatus(t, resp) + if resp.Error.Message != "S3 Bucket is required" { + t.Fatal("should return error - missing s3 bucket") + } + + config.FileSettings.AmazonS3Bucket = model.MINIO_BUCKET + _, resp = th.SystemAdminClient.TestS3Connection(&config) + CheckBadRequestStatus(t, resp) + if resp.Error.Message != "S3 Endpoint is required" { + t.Fatal("should return error - missing s3 endpoint") + } + + config.FileSettings.AmazonS3Endpoint = s3Endpoint + _, resp = th.SystemAdminClient.TestS3Connection(&config) + CheckBadRequestStatus(t, resp) + if resp.Error.Message != "S3 Region is required" { + t.Fatal("should return error - missing s3 region") + } + + config.FileSettings.AmazonS3Region = "us-east-1" + _, resp = th.SystemAdminClient.TestS3Connection(&config) + CheckOKStatus(t, resp) + + config.FileSettings.AmazonS3Bucket = "Wrong_bucket" + _, resp = th.SystemAdminClient.TestS3Connection(&config) + CheckInternalErrorStatus(t, resp) + if resp.Error.Message != "Error checking if bucket exists." { + t.Fatal("should return error ") + } +} diff --git a/api4/team.go b/api4/team.go index d770aee22..8e4c5c312 100644 --- a/api4/team.go +++ b/api4/team.go @@ -6,6 +6,7 @@ package api4 import ( "bytes" "encoding/base64" + "fmt" "net/http" "strconv" @@ -28,6 +29,10 @@ func (api *API) InitTeam() { api.BaseRoutes.Team.Handle("", api.ApiSessionRequired(deleteTeam)).Methods("DELETE") api.BaseRoutes.Team.Handle("/patch", api.ApiSessionRequired(patchTeam)).Methods("PUT") api.BaseRoutes.Team.Handle("/stats", api.ApiSessionRequired(getTeamStats)).Methods("GET") + + api.BaseRoutes.Team.Handle("/image", api.ApiSessionRequiredTrustRequester(getTeamIcon)).Methods("GET") + api.BaseRoutes.Team.Handle("/image", api.ApiSessionRequired(setTeamIcon)).Methods("POST") + api.BaseRoutes.TeamMembers.Handle("", api.ApiSessionRequired(getTeamMembers)).Methods("GET") api.BaseRoutes.TeamMembers.Handle("/ids", api.ApiSessionRequired(getTeamMembersByIds)).Methods("POST") api.BaseRoutes.TeamMembersForUser.Handle("", api.ApiSessionRequired(getTeamMembersForUser)).Methods("GET") @@ -729,3 +734,81 @@ func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(result))) } } + +func getTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { + c.SetPermissionError(model.PERMISSION_VIEW_TEAM) + return + } + + if team, err := c.App.GetTeam(c.Params.TeamId); err != nil { + c.Err = err + return + } else { + etag := strconv.FormatInt(team.LastTeamIconUpdate, 10) + + if c.HandleEtag(etag, "Get Team Icon", w, r) { + return + } + + if img, err := c.App.GetTeamIcon(team); err != nil { + c.Err = err + return + } else { + w.Header().Set("Content-Type", "image/png") + w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%v, public", 24*60*60)) // 24 hrs + w.Header().Set(model.HEADER_ETAG_SERVER, etag) + w.Write(img) + } + } +} + +func setTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) { + c.SetPermissionError(model.PERMISSION_MANAGE_TEAM) + return + } + + if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize { + c.Err = model.NewAppError("setTeamIcon", "api.team.set_team_icon.too_large.app_error", nil, "", http.StatusBadRequest) + return + } + + if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil { + c.Err = model.NewAppError("setTeamIcon", "api.team.set_team_icon.parse.app_error", nil, err.Error(), http.StatusBadRequest) + return + } + + m := r.MultipartForm + + imageArray, ok := m.File["image"] + if !ok { + c.Err = model.NewAppError("setTeamIcon", "api.team.set_team_icon.no_file.app_error", nil, "", http.StatusBadRequest) + return + } + + if len(imageArray) <= 0 { + c.Err = model.NewAppError("setTeamIcon", "api.team.set_team_icon.array.app_error", nil, "", http.StatusBadRequest) + return + } + + imageData := imageArray[0] + + if err := c.App.SetTeamIcon(c.Params.TeamId, imageData); err != nil { + c.Err = err + return + } + + c.LogAudit("") + ReturnStatusOK(w) +} diff --git a/api4/team_test.go b/api4/team_test.go index faa90e511..04a0e9ae4 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -15,6 +15,8 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCreateTeam(t *testing.T) { @@ -1915,3 +1917,82 @@ func TestGetTeamInviteInfo(t *testing.T) { _, resp = Client.GetTeamInviteInfo("junk") CheckNotFoundStatus(t, resp) } + +func TestSetTeamIcon(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + Client := th.Client + team := th.BasicTeam + + data, err := readTestFile("test.png") + if err != nil { + t.Fatal(err) + } + + th.LoginTeamAdmin() + + ok, resp := Client.SetTeamIcon(team.Id, data) + if !ok { + t.Fatal(resp.Error) + } + CheckNoError(t, resp) + + ok, resp = Client.SetTeamIcon(model.NewId(), data) + if ok { + t.Fatal("Should return false, set team icon not allowed") + } + CheckForbiddenStatus(t, resp) + + th.LoginBasic() + + _, resp = Client.SetTeamIcon(team.Id, data) + if resp.StatusCode == http.StatusForbidden { + CheckForbiddenStatus(t, resp) + } else if resp.StatusCode == http.StatusUnauthorized { + CheckUnauthorizedStatus(t, resp) + } else { + t.Fatal("Should have failed either forbidden or unauthorized") + } + + Client.Logout() + + _, resp = Client.SetTeamIcon(team.Id, data) + if resp.StatusCode == http.StatusForbidden { + CheckForbiddenStatus(t, resp) + } else if resp.StatusCode == http.StatusUnauthorized { + CheckUnauthorizedStatus(t, resp) + } else { + t.Fatal("Should have failed either forbidden or unauthorized") + } + + teamBefore, err := th.App.GetTeam(team.Id) + require.Nil(t, err) + + _, resp = th.SystemAdminClient.SetTeamIcon(team.Id, data) + CheckNoError(t, resp) + + teamAfter, err := th.App.GetTeam(team.Id) + require.Nil(t, err) + assert.True(t, teamBefore.LastTeamIconUpdate < teamAfter.LastTeamIconUpdate, "LastTeamIconUpdate should have been updated for team") + + info := &model.FileInfo{Path: "teams/" + team.Id + "/teamIcon.png"} + if err := th.cleanupTestFile(info); err != nil { + t.Fatal(err) + } +} + +func TestGetTeamIcon(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + Client := th.Client + team := th.BasicTeam + + // should always fail because no initial image and no auto creation + _, resp := Client.GetTeamIcon(team.Id, "") + CheckNotFoundStatus(t, resp) + + Client.Logout() + + _, resp = Client.GetTeamIcon(team.Id, "") + CheckUnauthorizedStatus(t, resp) +} diff --git a/api4/webhook.go b/api4/webhook.go index e19f14704..853cf43f3 100644 --- a/api4/webhook.go +++ b/api4/webhook.go @@ -510,7 +510,6 @@ func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) { } func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) { - decodeError := &model.AppError{} incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload) if decodeError != nil { |