MM-8708 Remove api package (#8784)
* Remove api package * Remove api dependency from cmd package * Remove EnableAPIv3 setting * Update web tests * Add more websocket tests * Move some ws and oauth tests to api4 package * Move command tests into api4 package * Test fixes * Fix msg command test * Add some app file tests
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
02f8c18f40
Коммит
1f6c271b3b
2
Makefile
2
Makefile
@@ -496,8 +496,6 @@ clean: stop-docker ## Clean up everything except persistant server data.
|
||||
rm -f mattermost.log
|
||||
rm -f mattermost.log.jsonl
|
||||
rm -f npm-debug.log
|
||||
rm -f api/mattermost.log
|
||||
rm -f api/mattermost.log.jsonl
|
||||
rm -f .prepare-go
|
||||
rm -f enterprise
|
||||
rm -f cover.out
|
||||
|
||||
422
api/admin.go
422
api/admin.go
@@ -1,422 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitAdmin() {
|
||||
api.BaseRoutes.Admin.Handle("/logs", api.ApiAdminSystemRequired(getLogs)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/audits", api.ApiAdminSystemRequired(getAllAudits)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/config", api.ApiAdminSystemRequired(getConfig)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/save_config", api.ApiAdminSystemRequired(saveConfig)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/reload_config", api.ApiAdminSystemRequired(reloadConfig)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/invalidate_all_caches", api.ApiAdminSystemRequired(invalidateAllCaches)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/test_email", api.ApiAdminSystemRequired(testEmail)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/recycle_db_conn", api.ApiAdminSystemRequired(recycleDatabaseConnection)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", api.ApiAdminSystemRequired(getAnalytics)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/analytics/{name:[A-Za-z0-9_]+}", api.ApiAdminSystemRequired(getAnalytics)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/save_compliance_report", api.ApiAdminSystemRequired(saveComplianceReport)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/compliance_reports", api.ApiAdminSystemRequired(getComplianceReports)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", api.ApiAdminSystemRequiredTrustRequester(downloadComplianceReport)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/upload_brand_image", api.ApiAdminSystemRequired(uploadBrandImage)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/get_brand_image", api.ApiAppHandlerTrustRequester(getBrandImage)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/reset_mfa", api.ApiAdminSystemRequired(adminResetMfa)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/reset_password", api.ApiAdminSystemRequired(adminResetPassword)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/ldap_sync_now", api.ApiAdminSystemRequired(ldapSyncNow)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/ldap_test", api.ApiAdminSystemRequired(ldapTest)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/saml_metadata", api.ApiAppHandler(samlMetadata)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/add_certificate", api.ApiAdminSystemRequired(addCertificate)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/remove_certificate", api.ApiAdminSystemRequired(removeCertificate)).Methods("POST")
|
||||
api.BaseRoutes.Admin.Handle("/saml_cert_status", api.ApiAdminSystemRequired(samlCertificateStatus)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/cluster_status", api.ApiAdminSystemRequired(getClusterStatus)).Methods("GET")
|
||||
api.BaseRoutes.Admin.Handle("/recently_active_users/{team_id:[A-Za-z0-9]+}", api.ApiUserRequired(getRecentlyActiveUsers)).Methods("GET")
|
||||
}
|
||||
|
||||
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
lines, err := c.App.GetLogs(0, 10000)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.ArrayToJson(lines)))
|
||||
}
|
||||
|
||||
func getClusterStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
infos := c.App.GetClusterStatus()
|
||||
|
||||
if c.App.Cluster != nil {
|
||||
w.Header().Set(model.HEADER_CLUSTER_ID, c.App.Cluster.GetClusterId())
|
||||
}
|
||||
|
||||
w.Write([]byte(model.ClusterInfosToJson(infos)))
|
||||
}
|
||||
|
||||
func getAllAudits(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if audits, err := c.App.GetAudits("", 200); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(audits.Etag(), "Get All Audits", w, r) {
|
||||
return
|
||||
} else {
|
||||
etag := audits.Etag()
|
||||
if len(etag) > 0 {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
}
|
||||
|
||||
w.Write([]byte(audits.ToJson()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
cfg := c.App.GetConfig()
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Write([]byte(cfg.ToJson()))
|
||||
}
|
||||
|
||||
func reloadConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.App.ReloadConfig()
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func invalidateAllCaches(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
err := c.App.InvalidateAllCaches()
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
cfg := model.ConfigFromJson(r.Body)
|
||||
if cfg == nil {
|
||||
c.SetInvalidParam("saveConfig", "config")
|
||||
return
|
||||
}
|
||||
|
||||
// Do not allow plugin uploads to be toggled through the API
|
||||
cfg.PluginSettings.EnableUploads = c.App.GetConfig().PluginSettings.EnableUploads
|
||||
|
||||
err := c.App.SaveConfig(cfg, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func recycleDatabaseConnection(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.App.RecycleDatabaseConnection()
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
cfg := model.ConfigFromJson(r.Body)
|
||||
if cfg == nil {
|
||||
c.SetInvalidParam("testEmail", "config")
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.TestEmail(c.Session.UserId, cfg)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
m := make(map[string]string)
|
||||
m["SUCCESS"] = "true"
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
|
||||
func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
crs, err := c.App.GetComplianceReports(0, 10000)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
w.Write([]byte(crs.ToJson()))
|
||||
}
|
||||
|
||||
func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
job := model.ComplianceFromJson(r.Body)
|
||||
if job == nil {
|
||||
c.SetInvalidParam("saveComplianceReport", "compliance")
|
||||
return
|
||||
}
|
||||
|
||||
job.UserId = c.Session.UserId
|
||||
|
||||
rjob, err := c.App.SaveComplianceReport(job)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
w.Write([]byte(rjob.ToJson()))
|
||||
}
|
||||
|
||||
func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("downloadComplianceReport", "id")
|
||||
return
|
||||
}
|
||||
|
||||
job, err := c.App.GetComplianceReport(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
reportBytes, err := c.App.GetComplianceFile(job)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("downloaded " + job.Desc)
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(reportBytes)))
|
||||
w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer
|
||||
|
||||
// attach extra headers to trigger a download on IE, Edge, and Safari
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"")
|
||||
|
||||
if ua.Browser.Name == uasurfer.BrowserIE || ua.Browser.Name == uasurfer.BrowserSafari {
|
||||
// trim off anything before the final / so we just get the file's name
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
}
|
||||
|
||||
w.Write(reportBytes)
|
||||
}
|
||||
|
||||
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
teamId := params["id"]
|
||||
name := params["name"]
|
||||
|
||||
rows, err := c.App.GetAnalytics(name, teamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if rows == nil {
|
||||
c.SetInvalidParam("getAnalytics", "name")
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(rows.ToJson()))
|
||||
}
|
||||
|
||||
func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil {
|
||||
c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.parse.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
|
||||
imageArray, ok := m.File["image"]
|
||||
if !ok {
|
||||
c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if len(imageArray) <= 0 {
|
||||
c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.array.app_error", nil, "", http.StatusBadRequest)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.SaveBrandImage(imageArray[0]); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if img, err := c.App.GetBrandImage(); err != nil {
|
||||
w.Write(nil)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Write(img)
|
||||
}
|
||||
}
|
||||
|
||||
func adminResetMfa(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
userId := props["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("adminResetMfa", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeactivateMfa(userId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func adminResetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
userId := props["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("adminResetPassword", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
newPassword := props["new_password"]
|
||||
if err := c.App.IsPasswordValid(newPassword); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.UpdatePasswordByUserIdSendEmail(userId, newPassword, c.T("api.user.reset_password.method")); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.App.SyncLdap()
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func ldapTest(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if err := c.App.TestLdap(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func samlMetadata(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if result, err := c.App.GetSamlMetadata(); err != nil {
|
||||
c.Err = model.NewAppError("loginWithSaml", "api.admin.saml.metadata.app_error", nil, "err="+err.Message, http.StatusInternalServerError)
|
||||
return
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"metadata.xml\"")
|
||||
w.Write([]byte(result))
|
||||
}
|
||||
}
|
||||
|
||||
func addCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
|
||||
fileArray, ok := m.File["certificate"]
|
||||
if !ok {
|
||||
c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if len(fileArray) <= 0 {
|
||||
c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.array.app_error", nil, "", http.StatusBadRequest)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
fileData := fileArray[0]
|
||||
|
||||
if err := app.WriteSamlFile(fileData); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func removeCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
if err := app.RemoveSamlFile(props["filename"]); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
status := c.App.GetSamlCertificateStatus()
|
||||
|
||||
statusMap := map[string]interface{}{}
|
||||
statusMap["IdpCertificateFile"] = status.IdpCertificateFile
|
||||
statusMap["PrivateKeyFile"] = status.PrivateKeyFile
|
||||
statusMap["PublicCertificateFile"] = status.PublicCertificateFile
|
||||
|
||||
w.Write([]byte(model.StringInterfaceToJson(statusMap)))
|
||||
}
|
||||
|
||||
func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if profiles, err := c.App.GetRecentlyActiveUsersForTeam(c.TeamId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
for _, p := range profiles {
|
||||
sanitizeProfile(c, p)
|
||||
}
|
||||
|
||||
w.Write([]byte(model.UserMapToJson(profiles)))
|
||||
}
|
||||
}
|
||||
@@ -1,624 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetLogs(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.GetLogs(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if logs, err := th.SystemAdminClient.GetLogs(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(logs.Data.([]string)) <= 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClusterInfos(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
th := Setup().InitSystemAdmin().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.GetClusterStatus(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.GetClusterStatus(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllAudits(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.GetAllAudits(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if audits, err := th.SystemAdminClient.GetAllAudits(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(audits.Data.(model.Audits)) <= 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.GetConfig(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if result, err := th.SystemAdminClient.GetConfig(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
cfg := result.Data.(*model.Config)
|
||||
|
||||
if len(cfg.TeamSettings.SiteName) == 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && len(*cfg.LdapSettings.BindPassword) != 0 {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if *cfg.FileSettings.PublicLinkSalt != model.FAKE_SETTING {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && len(cfg.FileSettings.AmazonS3SecretAccessKey) != 0 {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if cfg.EmailSettings.InviteSalt != model.FAKE_SETTING {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && len(cfg.EmailSettings.SMTPPassword) != 0 {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if cfg.GitLabSettings.Secret != model.FAKE_SETTING && len(cfg.GitLabSettings.Secret) != 0 {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if *cfg.SqlSettings.DataSource != model.FAKE_SETTING {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if cfg.SqlSettings.AtRestEncryptKey != model.FAKE_SETTING {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 {
|
||||
t.Fatal("did not sanitize properly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReloadConfig(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.ReloadConfig(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.ReloadConfig(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
}
|
||||
|
||||
func TestInvalidateAllCache(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.InvalidateAllCaches(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.InvalidateAllCaches(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveConfig(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.SaveConfig(th.App.Config()); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = false })
|
||||
|
||||
if _, err := th.SystemAdminClient.SaveConfig(th.App.Config()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
|
||||
// Should not be able to modify PluginSettings.EnableUploads
|
||||
oldEnableUploads := *th.App.GetConfig().PluginSettings.EnableUploads
|
||||
cfg := &model.Config{}
|
||||
cfg.SetDefaults()
|
||||
*cfg.PluginSettings.EnableUploads = !oldEnableUploads
|
||||
|
||||
if _, err := th.SystemAdminClient.SaveConfig(cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
||||
}
|
||||
|
||||
func TestRecycleDatabaseConnection(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.RecycleDatabaseConnection(); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.RecycleDatabaseConnection(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailTest(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
SendEmailNotifications := th.App.Config().EmailSettings.SendEmailNotifications
|
||||
SMTPServer := th.App.Config().EmailSettings.SMTPServer
|
||||
SMTPPort := th.App.Config().EmailSettings.SMTPPort
|
||||
FeedbackEmail := th.App.Config().EmailSettings.FeedbackEmail
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SendEmailNotifications = SendEmailNotifications })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SMTPServer = SMTPServer })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SMTPPort = SMTPPort })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.FeedbackEmail = FeedbackEmail })
|
||||
}()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SendEmailNotifications = false })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SMTPServer = "" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.SMTPPort = "" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.EmailSettings.FeedbackEmail = "" })
|
||||
|
||||
if _, err := th.BasicClient.TestEmail(th.App.Config()); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.TestEmail(th.App.Config()); err == nil {
|
||||
t.Fatal("should have errored")
|
||||
} else {
|
||||
if err.Id != "api.admin.test_email.missing_server" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLdapTest(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.TestLdap(th.App.Config()); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.TestLdap(th.App.Config()); err == nil {
|
||||
t.Fatal("should have errored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTeamAnalyticsStandard(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.CreatePrivateChannel(th.BasicClient, th.BasicTeam)
|
||||
|
||||
if _, err := th.BasicClient.GetTeamAnalytics(th.BasicTeam.Id, "standard"); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
maxUsersForStats := *th.App.Config().AnalyticsSettings.MaxUsersForStatistics
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = maxUsersForStats })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = 1000000 })
|
||||
|
||||
if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "standard"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[0].Name != "channel_open_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[0].Value != 4 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Name != "channel_private_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Value != 1 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Name != "post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Value != 9 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Name != "unique_user_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Value != 2 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Name != "team_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := th.SystemAdminClient.GetSystemAnalytics("standard"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[0].Name != "channel_open_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[0].Value < 3 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Name != "channel_private_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Name != "post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Name != "unique_user_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Name != "team_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = 1 })
|
||||
|
||||
if result, err := th.SystemAdminClient.GetSystemAnalytics("standard"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[2].Name != "post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Value != -1 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTeamAnalyticsExtra(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.CreatePost(th.BasicClient, th.BasicChannel)
|
||||
|
||||
if _, err := th.BasicClient.GetTeamAnalytics("", "extra_counts"); err == nil {
|
||||
t.Fatal("Shouldn't have permissions")
|
||||
}
|
||||
|
||||
maxUsersForStats := *th.App.Config().AnalyticsSettings.MaxUsersForStatistics
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = maxUsersForStats })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = 1000000 })
|
||||
|
||||
if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "extra_counts"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[0].Name != "file_post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[0].Value != 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Name != "hashtag_post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Value != 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Name != "incoming_webhook_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Value != 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Name != "outgoing_webhook_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Value != 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Name != "command_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Value != 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[5].Name != "session_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[5].Value == 0 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := th.SystemAdminClient.GetSystemAnalytics("extra_counts"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[0].Name != "file_post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Name != "hashtag_post_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[2].Name != "incoming_webhook_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[3].Name != "outgoing_webhook_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[4].Name != "command_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[5].Name != "session_count" {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.AnalyticsSettings.MaxUsersForStatistics = 1 })
|
||||
|
||||
if result, err := th.SystemAdminClient.GetSystemAnalytics("extra_counts"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rows := result.Data.(model.AnalyticsRows)
|
||||
|
||||
if rows[0].Value != -1 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if rows[1].Value != -1 {
|
||||
t.Log(rows.ToJson())
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminResetMfa(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if _, err := th.BasicClient.AdminResetMfa("12345678901234567890123456"); err == nil {
|
||||
t.Fatal("should have failed - not an admin")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.AdminResetMfa(""); err == nil {
|
||||
t.Fatal("should have failed - empty user id")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.AdminResetMfa("12345678901234567890123456"); err == nil {
|
||||
t.Fatal("should have failed - bad user id")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.AdminResetMfa(th.BasicUser.Id); err == nil {
|
||||
t.Fatal("should have failed - not licensed or configured")
|
||||
}
|
||||
|
||||
// need to add more test cases when enterprise bits can be loaded into tests
|
||||
}
|
||||
|
||||
func TestAdminResetPassword(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
|
||||
user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
th.LinkUserToTeam(user, team)
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
if _, err := Client.AdminResetPassword("", "newpwd1"); err == nil {
|
||||
t.Fatal("Should have errored - empty user id")
|
||||
}
|
||||
|
||||
if _, err := Client.AdminResetPassword("123", "newpwd1"); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
if _, err := Client.AdminResetPassword("12345678901234567890123456", "newpwd1"); err == nil {
|
||||
t.Fatal("Should have errored - bad user id")
|
||||
}
|
||||
|
||||
if _, err := Client.AdminResetPassword("12345678901234567890123456", "newp"); err == nil {
|
||||
t.Fatal("Should have errored - password too short")
|
||||
}
|
||||
|
||||
authData := model.NewId()
|
||||
user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", AuthData: &authData, AuthService: "random"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(user2.Id))
|
||||
|
||||
if _, err := Client.AdminResetPassword(user.Id, "newpwd1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user.Id, "newpwd1"))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.AdminResetPassword(user.Id, "newpwd1"); err == nil {
|
||||
t.Fatal("Should have errored - not system admin")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminLdapSyncNow(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
|
||||
if _, err := Client.LdapSyncNow(); err != nil {
|
||||
t.Fatal("Returned Failure")
|
||||
}
|
||||
}
|
||||
|
||||
// Needs more work
|
||||
func TestGetRecentlyActiveUsers(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if userMap, err := th.BasicClient.GetRecentlyActiveUsers(th.BasicTeam.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(userMap.Data.(map[string]*model.User)) >= 2 {
|
||||
t.Fatal("should have been at least 2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableAPIv3(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
enableAPIv3 := *th.App.Config().ServiceSettings.EnableAPIv3
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableAPIv3 = enableAPIv3 })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableAPIv3 = false })
|
||||
|
||||
_, err := Client.GetUser(th.BasicUser.Id, "")
|
||||
|
||||
if err.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("wrong error code")
|
||||
}
|
||||
|
||||
if err.Id != "api.context.v3_disabled.app_error" {
|
||||
t.Fatal("wrong error message")
|
||||
}
|
||||
}
|
||||
131
api/api.go
131
api/api.go
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
|
||||
_ "github.com/nicksnyder/go-i18n/i18n"
|
||||
)
|
||||
|
||||
type Routes struct {
|
||||
Root *mux.Router // ''
|
||||
ApiRoot *mux.Router // 'api/v3'
|
||||
|
||||
Users *mux.Router // 'api/v3/users'
|
||||
NeedUser *mux.Router // 'api/v3/users/{user_id:[A-Za-z0-9]+}'
|
||||
|
||||
Teams *mux.Router // 'api/v3/teams'
|
||||
NeedTeam *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}'
|
||||
|
||||
Channels *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels'
|
||||
NeedChannel *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}'
|
||||
NeedChannelName *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/name/{channel_name:[A-Za-z0-9_-]+}'
|
||||
|
||||
Posts *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts'
|
||||
NeedPost *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts/{post_id:[A-Za-z0-9]+}'
|
||||
|
||||
Commands *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/commands'
|
||||
Hooks *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/hooks'
|
||||
|
||||
TeamFiles *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/files'
|
||||
Files *mux.Router // 'api/v3/files'
|
||||
NeedFile *mux.Router // 'api/v3/files/{file_id:[A-Za-z0-9]+}'
|
||||
|
||||
OAuth *mux.Router // 'api/v3/oauth'
|
||||
|
||||
Admin *mux.Router // 'api/v3/admin'
|
||||
|
||||
General *mux.Router // 'api/v3/general'
|
||||
|
||||
Preferences *mux.Router // 'api/v3/preferences'
|
||||
|
||||
License *mux.Router // 'api/v3/license'
|
||||
|
||||
Public *mux.Router // 'api/v3/public'
|
||||
|
||||
Emoji *mux.Router // 'api/v3/emoji'
|
||||
|
||||
Webrtc *mux.Router // 'api/v3/webrtc'
|
||||
}
|
||||
|
||||
type API struct {
|
||||
App *app.App
|
||||
BaseRoutes *Routes
|
||||
}
|
||||
|
||||
func Init(a *app.App, root *mux.Router) *API {
|
||||
api := &API{
|
||||
App: a,
|
||||
BaseRoutes: &Routes{},
|
||||
}
|
||||
api.BaseRoutes.Root = root
|
||||
api.BaseRoutes.ApiRoot = root.PathPrefix(model.API_URL_SUFFIX_V3).Subrouter()
|
||||
api.BaseRoutes.Users = api.BaseRoutes.ApiRoot.PathPrefix("/users").Subrouter()
|
||||
api.BaseRoutes.NeedUser = api.BaseRoutes.Users.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.Teams = api.BaseRoutes.ApiRoot.PathPrefix("/teams").Subrouter()
|
||||
api.BaseRoutes.NeedTeam = api.BaseRoutes.Teams.PathPrefix("/{team_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.Channels = api.BaseRoutes.NeedTeam.PathPrefix("/channels").Subrouter()
|
||||
api.BaseRoutes.NeedChannel = api.BaseRoutes.Channels.PathPrefix("/{channel_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.NeedChannelName = api.BaseRoutes.Channels.PathPrefix("/name/{channel_name:[A-Za-z0-9_-]+}").Subrouter()
|
||||
api.BaseRoutes.Posts = api.BaseRoutes.NeedChannel.PathPrefix("/posts").Subrouter()
|
||||
api.BaseRoutes.NeedPost = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.Commands = api.BaseRoutes.NeedTeam.PathPrefix("/commands").Subrouter()
|
||||
api.BaseRoutes.TeamFiles = api.BaseRoutes.NeedTeam.PathPrefix("/files").Subrouter()
|
||||
api.BaseRoutes.Files = api.BaseRoutes.ApiRoot.PathPrefix("/files").Subrouter()
|
||||
api.BaseRoutes.NeedFile = api.BaseRoutes.Files.PathPrefix("/{file_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.Hooks = api.BaseRoutes.NeedTeam.PathPrefix("/hooks").Subrouter()
|
||||
api.BaseRoutes.OAuth = api.BaseRoutes.ApiRoot.PathPrefix("/oauth").Subrouter()
|
||||
api.BaseRoutes.Admin = api.BaseRoutes.ApiRoot.PathPrefix("/admin").Subrouter()
|
||||
api.BaseRoutes.General = api.BaseRoutes.ApiRoot.PathPrefix("/general").Subrouter()
|
||||
api.BaseRoutes.Preferences = api.BaseRoutes.ApiRoot.PathPrefix("/preferences").Subrouter()
|
||||
api.BaseRoutes.License = api.BaseRoutes.ApiRoot.PathPrefix("/license").Subrouter()
|
||||
api.BaseRoutes.Public = api.BaseRoutes.ApiRoot.PathPrefix("/public").Subrouter()
|
||||
api.BaseRoutes.Emoji = api.BaseRoutes.ApiRoot.PathPrefix("/emoji").Subrouter()
|
||||
api.BaseRoutes.Webrtc = api.BaseRoutes.ApiRoot.PathPrefix("/webrtc").Subrouter()
|
||||
|
||||
api.InitUser()
|
||||
api.InitTeam()
|
||||
api.InitChannel()
|
||||
api.InitPost()
|
||||
api.InitWebSocket()
|
||||
api.InitFile()
|
||||
api.InitCommand()
|
||||
api.InitAdmin()
|
||||
api.InitGeneral()
|
||||
api.InitOAuth()
|
||||
api.InitWebhook()
|
||||
api.InitPreference()
|
||||
api.InitLicense()
|
||||
api.InitEmoji()
|
||||
api.InitStatus()
|
||||
api.InitWebrtc()
|
||||
api.InitReaction()
|
||||
|
||||
// 404 on any api route before web.go has a chance to serve it
|
||||
root.Handle("/api/{anything:.*}", http.HandlerFunc(api.Handle404))
|
||||
|
||||
a.InitEmailBatching()
|
||||
|
||||
if *a.Config().ServiceSettings.EnableAPIv3 {
|
||||
mlog.Info("API version 3 is scheduled for deprecation. Please see https://api.mattermost.com for details.")
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func (api *API) Handle404(w http.ResponseWriter, r *http.Request) {
|
||||
Handle404(api.App, w, r)
|
||||
}
|
||||
|
||||
func ReturnStatusOK(w http.ResponseWriter) {
|
||||
m := make(map[string]string)
|
||||
m[model.STATUS] = model.STATUS_OK
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/store/storetest"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
|
||||
// Setup a global logger to catch tests logging outside of app context
|
||||
// The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen.
|
||||
mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{
|
||||
EnableConsole: true,
|
||||
ConsoleJson: true,
|
||||
ConsoleLevel: "error",
|
||||
EnableFile: false,
|
||||
}))
|
||||
|
||||
utils.TranslationsPreInit()
|
||||
|
||||
// In the case where a dev just wants to run a single test, it's faster to just use the default
|
||||
// store.
|
||||
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
|
||||
mlog.Info("-test.run used, not creating temporary containers")
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
status := 0
|
||||
|
||||
container, settings, err := storetest.NewMySQLContainer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
UseTestStore(container, settings)
|
||||
|
||||
defer func() {
|
||||
StopTestStore()
|
||||
os.Exit(status)
|
||||
}()
|
||||
|
||||
status = m.Run()
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/store/storetest"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"github.com/mattermost/mattermost-server/wsapi"
|
||||
)
|
||||
|
||||
type TestHelper struct {
|
||||
App *app.App
|
||||
tempConfigPath string
|
||||
|
||||
BasicClient *model.Client
|
||||
BasicTeam *model.Team
|
||||
BasicUser *model.User
|
||||
BasicUser2 *model.User
|
||||
BasicChannel *model.Channel
|
||||
BasicPost *model.Post
|
||||
PinnedPost *model.Post
|
||||
|
||||
SystemAdminClient *model.Client
|
||||
SystemAdminTeam *model.Team
|
||||
SystemAdminUser *model.User
|
||||
SystemAdminChannel *model.Channel
|
||||
}
|
||||
|
||||
type persistentTestStore struct {
|
||||
store.Store
|
||||
}
|
||||
|
||||
func (*persistentTestStore) Close() {}
|
||||
|
||||
var testStoreContainer *storetest.RunningContainer
|
||||
var testStore *persistentTestStore
|
||||
|
||||
// UseTestStore sets the container and corresponding settings to use for tests. Once the tests are
|
||||
// complete (e.g. at the end of your TestMain implementation), you should call StopTestStore.
|
||||
func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) {
|
||||
testStoreContainer = container
|
||||
testStore = &persistentTestStore{store.NewLayeredStore(sqlstore.NewSqlSupplier(*settings, nil), nil, nil)}
|
||||
}
|
||||
|
||||
func StopTestStore() {
|
||||
if testStoreContainer != nil {
|
||||
testStoreContainer.Stop()
|
||||
testStoreContainer = nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestHelper(enterprise bool) *TestHelper {
|
||||
permConfig, err := os.Open(utils.FindConfigFile("config.json"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer permConfig.Close()
|
||||
tempConfig, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(tempConfig, permConfig)
|
||||
tempConfig.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
options := []app.Option{app.ConfigFile(tempConfig.Name()), app.DisableConfigWatch}
|
||||
if testStore != nil {
|
||||
options = append(options, app.StoreOverride(testStore))
|
||||
}
|
||||
|
||||
a, err := app.New(options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
th := &TestHelper{
|
||||
App: a,
|
||||
tempConfigPath: tempConfig.Name(),
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.MaxUsersPerTeam = 50
|
||||
*cfg.RateLimitSettings.Enable = false
|
||||
cfg.EmailSettings.SendEmailNotifications = true
|
||||
*cfg.ServiceSettings.EnableAPIv3 = true
|
||||
})
|
||||
prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress
|
||||
if testStore != nil {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||
}
|
||||
serverErr := th.App.StartServer()
|
||||
if serverErr != nil {
|
||||
panic(serverErr)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||
api4.Init(th.App, th.App.Srv.Router, false)
|
||||
Init(th.App, th.App.Srv.Router)
|
||||
wsapi.Init(th.App, th.App.Srv.WebSocketRouter)
|
||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||
th.App.DoAdvancedPermissionsMigration()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
|
||||
if enterprise {
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
} else {
|
||||
th.App.SetLicense(nil)
|
||||
}
|
||||
|
||||
return th
|
||||
}
|
||||
|
||||
func SetupEnterprise() *TestHelper {
|
||||
return setupTestHelper(true)
|
||||
}
|
||||
|
||||
func Setup() *TestHelper {
|
||||
return setupTestHelper(false)
|
||||
}
|
||||
|
||||
func (me *TestHelper) InitBasic() *TestHelper {
|
||||
me.waitForConnectivity()
|
||||
|
||||
me.BasicClient = me.CreateClient()
|
||||
me.BasicUser = me.CreateUser(me.BasicClient)
|
||||
me.App.UpdateUserRoles(me.BasicUser.Id, model.SYSTEM_USER_ROLE_ID, false)
|
||||
me.LoginBasic()
|
||||
me.BasicTeam = me.CreateTeam(me.BasicClient)
|
||||
me.LinkUserToTeam(me.BasicUser, me.BasicTeam)
|
||||
me.UpdateUserToNonTeamAdmin(me.BasicUser, me.BasicTeam)
|
||||
me.BasicUser2 = me.CreateUser(me.BasicClient)
|
||||
me.LinkUserToTeam(me.BasicUser2, me.BasicTeam)
|
||||
me.BasicClient.SetTeamId(me.BasicTeam.Id)
|
||||
me.BasicChannel = me.CreateChannel(me.BasicClient, me.BasicTeam)
|
||||
me.BasicPost = me.CreatePost(me.BasicClient, me.BasicChannel)
|
||||
|
||||
pinnedPostChannel := me.CreateChannel(me.BasicClient, me.BasicTeam)
|
||||
me.PinnedPost = me.CreatePinnedPost(me.BasicClient, pinnedPostChannel)
|
||||
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) InitSystemAdmin() *TestHelper {
|
||||
me.waitForConnectivity()
|
||||
|
||||
me.SystemAdminClient = me.CreateClient()
|
||||
me.SystemAdminUser = me.CreateUser(me.SystemAdminClient)
|
||||
me.SystemAdminUser.Password = "Password1"
|
||||
me.LoginSystemAdmin()
|
||||
me.SystemAdminTeam = me.CreateTeam(me.SystemAdminClient)
|
||||
me.LinkUserToTeam(me.SystemAdminUser, me.SystemAdminTeam)
|
||||
me.SystemAdminClient.SetTeamId(me.SystemAdminTeam.Id)
|
||||
me.App.UpdateUserRoles(me.SystemAdminUser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_ADMIN_ROLE_ID, false)
|
||||
me.SystemAdminChannel = me.CreateChannel(me.SystemAdminClient, me.SystemAdminTeam)
|
||||
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) waitForConnectivity() {
|
||||
for i := 0; i < 1000; i++ {
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", me.App.Srv.ListenAddr.Port))
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 20)
|
||||
}
|
||||
panic("unable to connect")
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateClient() *model.Client {
|
||||
return model.NewClient(fmt.Sprintf("http://localhost:%v", me.App.Srv.ListenAddr.Port))
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, *model.AppError) {
|
||||
return model.NewWebSocketClient(fmt.Sprintf("ws://localhost:%v", me.App.Srv.ListenAddr.Port), me.BasicClient.AuthToken)
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateTeam(client *model.Client) *model.Team {
|
||||
id := model.NewId()
|
||||
team := &model.Team{
|
||||
DisplayName: "dn_" + id,
|
||||
Name: GenerateTestTeamName(),
|
||||
Email: me.GenerateTestEmail(),
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
r := client.Must(client.CreateTeam(team)).Data.(*model.Team)
|
||||
utils.EnableDebugLogForTest()
|
||||
return r
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateUser(client *model.Client) *model.User {
|
||||
id := model.NewId()
|
||||
|
||||
user := &model.User{
|
||||
Email: me.GenerateTestEmail(),
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
Password: "Password1",
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
ruser := client.Must(client.CreateUser(user, "")).Data.(*model.User)
|
||||
ruser.Password = "Password1"
|
||||
store.Must(me.App.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
utils.EnableDebugLogForTest()
|
||||
return ruser
|
||||
}
|
||||
|
||||
func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
err := me.App.JoinUserToTeam(team, user, "")
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID}
|
||||
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
mlog.Error(tmr.Err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(tmr.Err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID}
|
||||
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
mlog.Error(tmr.Err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(tmr.Err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Channel) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
|
||||
cm := cmr.Data.(*model.ChannelMember)
|
||||
cm.Roles = "channel_admin channel_user"
|
||||
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(sr.Err)
|
||||
}
|
||||
} else {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(cmr.Err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeUserChannelUser(user *model.User, channel *model.Channel) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
|
||||
cm := cmr.Data.(*model.ChannelMember)
|
||||
cm.Roles = "channel_user"
|
||||
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(sr.Err)
|
||||
}
|
||||
} else {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(cmr.Err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateChannel(client *model.Client, team *model.Team) *model.Channel {
|
||||
return me.createChannel(client, team, model.CHANNEL_OPEN)
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePrivateChannel(client *model.Client, team *model.Team) *model.Channel {
|
||||
return me.createChannel(client, team, model.CHANNEL_PRIVATE)
|
||||
}
|
||||
|
||||
func (me *TestHelper) createChannel(client *model.Client, team *model.Team, channelType string) *model.Channel {
|
||||
id := model.NewId()
|
||||
|
||||
channel := &model.Channel{
|
||||
DisplayName: "dn_" + id,
|
||||
Name: "name_" + id,
|
||||
Type: channelType,
|
||||
TeamId: team.Id,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
r := client.Must(client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
utils.EnableDebugLogForTest()
|
||||
return r
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePost(client *model.Client, channel *model.Channel) *model.Post {
|
||||
id := model.NewId()
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: "message_" + id,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
r := client.Must(client.CreatePost(post)).Data.(*model.Post)
|
||||
utils.EnableDebugLogForTest()
|
||||
return r
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePinnedPost(client *model.Client, channel *model.Channel) *model.Post {
|
||||
id := model.NewId()
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: "message_" + id,
|
||||
IsPinned: true,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
r := client.Must(client.CreatePost(post)).Data.(*model.Post)
|
||||
utils.EnableDebugLogForTest()
|
||||
return r
|
||||
}
|
||||
|
||||
func (me *TestHelper) LoginBasic() {
|
||||
utils.DisableDebugLogForTest()
|
||||
me.BasicClient.Must(me.BasicClient.Login(me.BasicUser.Email, me.BasicUser.Password))
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) LoginBasic2() {
|
||||
utils.DisableDebugLogForTest()
|
||||
me.BasicClient.Must(me.BasicClient.Login(me.BasicUser2.Email, me.BasicUser2.Password))
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) LoginSystemAdmin() {
|
||||
utils.DisableDebugLogForTest()
|
||||
me.SystemAdminClient.Must(me.SystemAdminClient.Login(me.SystemAdminUser.Email, me.SystemAdminUser.Password))
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) GenerateTestEmail() string {
|
||||
if me.App.Config().EmailSettings.SMTPServer != "dockerhost" && os.Getenv("CI_INBUCKET_PORT") == "" {
|
||||
return strings.ToLower("success+" + model.NewId() + "@simulator.amazonses.com")
|
||||
}
|
||||
return strings.ToLower(model.NewId() + "@dockerhost")
|
||||
}
|
||||
|
||||
func GenerateTestTeamName() string {
|
||||
return "faketeam" + model.NewRandomString(6)
|
||||
}
|
||||
|
||||
func (me *TestHelper) TearDown() {
|
||||
me.App.Shutdown()
|
||||
os.Remove(me.tempConfigPath)
|
||||
if err := recover(); err != nil {
|
||||
StopTestStore()
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *TestHelper) SaveDefaultRolePermissions() map[string][]string {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
results := make(map[string][]string)
|
||||
|
||||
for _, roleName := range []string{
|
||||
"system_user",
|
||||
"system_admin",
|
||||
"team_user",
|
||||
"team_admin",
|
||||
"channel_user",
|
||||
"channel_admin",
|
||||
} {
|
||||
role, err1 := me.App.GetRoleByName(roleName)
|
||||
if err1 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err1)
|
||||
}
|
||||
|
||||
results[roleName] = role.Permissions
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
return results
|
||||
}
|
||||
|
||||
func (me *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
for roleName, permissions := range data {
|
||||
role, err1 := me.App.GetRoleByName(roleName)
|
||||
if err1 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err1)
|
||||
}
|
||||
|
||||
if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") {
|
||||
continue
|
||||
}
|
||||
|
||||
role.Permissions = permissions
|
||||
|
||||
_, err2 := me.App.UpdateRole(role)
|
||||
if err2 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err2)
|
||||
}
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
role, err1 := me.App.GetRoleByName(roleName)
|
||||
if err1 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err1)
|
||||
}
|
||||
|
||||
var newPermissions []string
|
||||
for _, p := range role.Permissions {
|
||||
if p != permission {
|
||||
newPermissions = append(newPermissions, p)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
||||
utils.EnableDebugLogForTest()
|
||||
return
|
||||
}
|
||||
|
||||
role.Permissions = newPermissions
|
||||
|
||||
_, err2 := me.App.UpdateRole(role)
|
||||
if err2 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err2)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
role, err1 := me.App.GetRoleByName(roleName)
|
||||
if err1 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err1)
|
||||
}
|
||||
|
||||
for _, existingPermission := range role.Permissions {
|
||||
if existingPermission == permission {
|
||||
utils.EnableDebugLogForTest()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
role.Permissions = append(role.Permissions, permission)
|
||||
|
||||
_, err2 := me.App.UpdateRole(role)
|
||||
if err2 != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(err2)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
843
api/channel.go
843
api/channel.go
@@ -1,843 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitChannel() {
|
||||
api.BaseRoutes.Channels.Handle("/", api.ApiUserRequired(getChannels)).Methods("GET")
|
||||
api.BaseRoutes.Channels.Handle("/more/{offset:[0-9]+}/{limit:[0-9]+}", api.ApiUserRequired(getMoreChannelsPage)).Methods("GET")
|
||||
api.BaseRoutes.Channels.Handle("/more/search", api.ApiUserRequired(searchMoreChannels)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/counts", api.ApiUserRequired(getChannelCounts)).Methods("GET")
|
||||
api.BaseRoutes.Channels.Handle("/members", api.ApiUserRequired(getMyChannelMembers)).Methods("GET")
|
||||
api.BaseRoutes.Channels.Handle("/create", api.ApiUserRequired(createChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/view", api.ApiUserRequired(viewChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/create_direct", api.ApiUserRequired(createDirectChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/create_group", api.ApiUserRequired(createGroupChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/update", api.ApiUserRequired(updateChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/update_header", api.ApiUserRequired(updateChannelHeader)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/update_purpose", api.ApiUserRequired(updateChannelPurpose)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/update_notify_props", api.ApiUserRequired(updateNotifyProps)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/autocomplete", api.ApiUserRequired(autocompleteChannels)).Methods("GET")
|
||||
api.BaseRoutes.Channels.Handle("/name/{channel_name:[A-Za-z0-9_-]+}", api.ApiUserRequired(getChannelByName)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.NeedChannelName.Handle("/join", api.ApiUserRequired(join)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.NeedChannel.Handle("/", api.ApiUserRequired(getChannel)).Methods("GET")
|
||||
api.BaseRoutes.NeedChannel.Handle("/stats", api.ApiUserRequired(getChannelStats)).Methods("GET")
|
||||
api.BaseRoutes.NeedChannel.Handle("/members/{user_id:[A-Za-z0-9]+}", api.ApiUserRequired(getChannelMember)).Methods("GET")
|
||||
api.BaseRoutes.NeedChannel.Handle("/members/ids", api.ApiUserRequired(getChannelMembersByIds)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/pinned", api.ApiUserRequired(getPinnedPosts)).Methods("GET")
|
||||
api.BaseRoutes.NeedChannel.Handle("/join", api.ApiUserRequired(join)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/leave", api.ApiUserRequired(leave)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/delete", api.ApiUserRequired(deleteChannel)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/add", api.ApiUserRequired(addMember)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/remove", api.ApiUserRequired(removeMember)).Methods("POST")
|
||||
api.BaseRoutes.NeedChannel.Handle("/update_member_roles", api.ApiUserRequired(updateChannelMemberRoles)).Methods("POST")
|
||||
}
|
||||
|
||||
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
channel := model.ChannelFromJson(r.Body)
|
||||
if channel == nil {
|
||||
c.SetInvalidParam("createChannel", "channel")
|
||||
return
|
||||
}
|
||||
|
||||
if len(channel.TeamId) == 0 {
|
||||
channel.TeamId = c.TeamId
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_PUBLIC_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_PUBLIC_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_PRIVATE && !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_PRIVATE_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_PRIVATE_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if sc, err := c.App.CreateChannelWithUser(channel, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(sc.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_CREATE_DIRECT_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_DIRECT_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
|
||||
userId := data["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("createDirectChannel", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if sc, err := c.App.CreateDirectChannel(c.Session.UserId, userId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(sc.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func createGroupChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_CREATE_GROUP_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_GROUP_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
userIds := model.ArrayFromJson(r.Body)
|
||||
if len(userIds) == 0 {
|
||||
c.SetInvalidParam("createGroupChannel", "user_ids")
|
||||
return
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, id := range userIds {
|
||||
if id == c.Session.UserId {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
userIds = append(userIds, c.Session.UserId)
|
||||
}
|
||||
|
||||
if sc, err := c.App.CreateGroupChannel(userIds, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(sc.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CanManageChannel(c *Context, channel *model.Channel) bool {
|
||||
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES)
|
||||
return false
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_PRIVATE && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
channel := model.ChannelFromJson(r.Body)
|
||||
|
||||
if channel == nil {
|
||||
c.SetInvalidParam("updateChannel", "channel")
|
||||
return
|
||||
}
|
||||
|
||||
var oldChannel *model.Channel
|
||||
var err *model.AppError
|
||||
if oldChannel, err = c.App.GetChannel(channel.Id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = c.App.GetChannelMember(channel.Id, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !CanManageChannel(c, channel) {
|
||||
return
|
||||
}
|
||||
|
||||
if oldChannel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("updateChannel", "api.channel.update_channel.deleted.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if oldChannel.Name == model.DEFAULT_CHANNEL {
|
||||
if (len(channel.Name) > 0 && channel.Name != oldChannel.Name) || (len(channel.Type) > 0 && channel.Type != oldChannel.Type) {
|
||||
c.Err = model.NewAppError("updateChannel", "api.channel.update_channel.tried.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
oldChannel.Header = channel.Header
|
||||
oldChannel.Purpose = channel.Purpose
|
||||
|
||||
oldChannelDisplayName := oldChannel.DisplayName
|
||||
|
||||
if len(channel.DisplayName) > 0 {
|
||||
oldChannel.DisplayName = channel.DisplayName
|
||||
}
|
||||
|
||||
if len(channel.Name) > 0 {
|
||||
oldChannel.Name = channel.Name
|
||||
}
|
||||
|
||||
if len(channel.Type) > 0 {
|
||||
oldChannel.Type = channel.Type
|
||||
}
|
||||
|
||||
if _, err := c.App.UpdateChannel(oldChannel); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if oldChannelDisplayName != channel.DisplayName {
|
||||
if err := c.App.PostUpdateChannelDisplayNameMessage(c.Session.UserId, channel, oldChannelDisplayName, channel.DisplayName); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(oldChannel.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
channelId := props["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateChannelHeader", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelHeader := props["channel_header"]
|
||||
if len(channelHeader) > 1024 {
|
||||
c.SetInvalidParam("updateChannelHeader", "channel_header")
|
||||
return
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(channelId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = c.App.GetChannelMember(channelId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !CanManageChannel(c, channel) {
|
||||
return
|
||||
}
|
||||
|
||||
oldChannelHeader := channel.Header
|
||||
channel.Header = channelHeader
|
||||
|
||||
if _, err := c.App.UpdateChannel(channel); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if err := c.App.PostUpdateChannelHeaderMessage(c.Session.UserId, channel, oldChannelHeader, channelHeader); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
channelId := props["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateChannelPurpose", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelPurpose := props["channel_purpose"]
|
||||
if len(channelPurpose) > 1024 {
|
||||
c.SetInvalidParam("updateChannelPurpose", "channel_purpose")
|
||||
return
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(channelId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = c.App.GetChannelMember(channelId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !CanManageChannel(c, channel) {
|
||||
return
|
||||
}
|
||||
|
||||
oldChannelPurpose := channel.Purpose
|
||||
channel.Purpose = channelPurpose
|
||||
|
||||
if _, err := c.App.UpdateChannel(channel); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if err := c.App.PostUpdateChannelPurposeMessage(c.Session.UserId, channel, oldChannelPurpose, channelPurpose); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.TeamId == "" {
|
||||
c.Err = model.NewAppError("", "api.context.missing_teamid.app_error", nil, "TeamIdRequired", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// user is already in the team
|
||||
// Get's all channels the user is a member of
|
||||
|
||||
if channels, err := c.App.GetChannelsForUser(c.TeamId, c.Session.UserId); err != nil {
|
||||
if err.Id == "store.sql_channel.get_channels.not_found.app_error" {
|
||||
// lets make sure the user is valid
|
||||
if _, err := c.App.GetUser(c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
c.RemoveSessionCookie(w, r)
|
||||
mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v forcing logout", c.Session.UserId), mlog.String("user_id", c.Session.UserId))
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(channels.Etag(), "Get Channels", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getMoreChannelsPage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getProfiles", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(params["limit"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getProfiles", "limit")
|
||||
return
|
||||
}
|
||||
|
||||
// user is already in the team
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_LIST_TEAM_CHANNELS) {
|
||||
c.SetPermissionError(model.PERMISSION_LIST_TEAM_CHANNELS)
|
||||
return
|
||||
}
|
||||
|
||||
if channels, err := c.App.GetChannelsUserNotIn(c.TeamId, c.Session.UserId, offset, limit); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelCounts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// user is already in the team
|
||||
|
||||
if counts, err := c.App.GetChannelCounts(c.TeamId, c.Session.UserId); err != nil {
|
||||
c.Err = model.NewAppError("getChannelCounts", "api.channel.get_channel_counts.app_error", nil, err.Message, http.StatusInternalServerError)
|
||||
return
|
||||
} else if c.HandleEtag(counts.Etag(), "Get Channel Counts", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, counts.Etag())
|
||||
w.Write([]byte(counts.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func join(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
channelName := params["channel_name"]
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channelId != "" {
|
||||
channel, err = c.App.GetChannel(channelId)
|
||||
} else if channelName != "" {
|
||||
channel, err = c.App.GetChannelByName(channelName, c.TeamId)
|
||||
} else {
|
||||
c.SetInvalidParam("join", "channel_id, channel_name")
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_JOIN_PUBLIC_CHANNELS) {
|
||||
c.SetPermissionError(model.PERMISSION_JOIN_PUBLIC_CHANNELS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = c.App.JoinChannel(channel, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
|
||||
func leave(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
id := params["channel_id"]
|
||||
|
||||
err := c.App.LeaveChannel(id, c.Session.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := mux.Vars(r)
|
||||
id := params["channel_id"]
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_PUBLIC_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_PRIVATE && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_DELETE_PRIVATE_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_PRIVATE_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
err = c.App.DeleteChannel(channel, c.Session.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = channel.Id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
func getChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["channel_id"]
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.TeamId != c.TeamId && !channel.IsGroupOrDirect() {
|
||||
c.Err = model.NewAppError("getChannel", "api.channel.get_channel.wrong_team.app_error", map[string]interface{}{"ChannelId": id, "TeamId": c.TeamId}, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var member *model.ChannelMember
|
||||
if member, err = c.App.GetChannelMember(id, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
data := &model.ChannelData{}
|
||||
data.Channel = channel
|
||||
data.Member = member
|
||||
|
||||
if c.HandleEtag(data.Etag(), "Get Channel", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, data.Etag())
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelName := params["channel_name"]
|
||||
|
||||
if channel, err := c.App.GetChannelByName(channelName, c.TeamId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.TeamId != c.TeamId && !channel.IsGroupOrDirect() {
|
||||
c.Err = model.NewAppError("getChannel", "api.channel.get_channel.wrong_team.app_error", map[string]interface{}{"ChannelName": channelName, "TeamId": c.TeamId}, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if c.HandleEtag(channel.Etag(), "Get Channel By Name", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, channel.Etag())
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelStats(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["channel_id"]
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.DeleteAt > 0 {
|
||||
c.Err = model.NewAppError("getChannelStats", "api.channel.get_channel_extra_info.deleted.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if memberCount, err := c.App.GetChannelMemberCount(id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
stats := model.ChannelStats{ChannelId: channel.Id, MemberCount: memberCount}
|
||||
w.Write([]byte(stats.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
userId := params["user_id"]
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if member, err := c.App.GetChannelMember(channelId, userId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(member.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getMyChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if members, err := c.App.GetChannelMembersForUser(c.TeamId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(members.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
|
||||
if result := <-c.App.Srv.Store.Channel().GetPinnedPosts(channelId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
posts := result.Data.(*model.PostList)
|
||||
w.Write([]byte(posts.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["channel_id"]
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
userId := data["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("addMember", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(id); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_PRIVATE && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS)
|
||||
return
|
||||
}
|
||||
|
||||
var nUser *model.User
|
||||
if nUser, err = c.App.GetUser(userId); err != nil {
|
||||
c.Err = model.NewAppError("addMember", "api.channel.add_member.find_user.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
cm, err := c.App.AddUserToChannel(nUser, channel)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name + " user_id=" + userId)
|
||||
|
||||
var oUser *model.User
|
||||
if oUser, err = c.App.GetUser(c.Session.UserId); err != nil {
|
||||
c.Err = model.NewAppError("addMember", "api.channel.add_member.user_adding.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c.App.Go(func() {
|
||||
c.App.PostAddToChannelMessage(oUser, nUser, channel, "")
|
||||
})
|
||||
|
||||
c.App.UpdateChannelLastViewedAt([]string{id}, oUser.Id)
|
||||
w.Write([]byte(cm.ToJson()))
|
||||
}
|
||||
|
||||
func removeMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
|
||||
data := model.MapFromJson(r.Body)
|
||||
userIdToRemove := data["user_id"]
|
||||
|
||||
if len(userIdToRemove) != 26 {
|
||||
c.SetInvalidParam("removeMember", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
if channel, err = c.App.GetChannel(channelId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_PRIVATE && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS)
|
||||
return
|
||||
}
|
||||
|
||||
if err = c.App.RemoveUserFromChannel(userIdToRemove, c.Session.UserId, channel); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("name=" + channel.Name + " user_id=" + userIdToRemove)
|
||||
|
||||
result := make(map[string]string)
|
||||
result["channel_id"] = channel.Id
|
||||
result["removed_user_id"] = userIdToRemove
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
|
||||
func updateNotifyProps(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
data := model.MapFromJson(r.Body)
|
||||
|
||||
userId := data["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("updateNotifyProps", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelId := data["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateNotifyProps", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToUser(c.Session, userId) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
|
||||
return
|
||||
}
|
||||
|
||||
member, err := c.App.UpdateChannelMemberNotifyProps(data, channelId, userId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(member.NotifyProps)))
|
||||
}
|
||||
|
||||
func searchMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.ChannelSearchFromJson(r.Body)
|
||||
if props == nil {
|
||||
c.SetInvalidParam("searchMoreChannels", "")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(props.Term) == 0 {
|
||||
c.SetInvalidParam("searchMoreChannels", "term")
|
||||
return
|
||||
}
|
||||
|
||||
if channels, err := c.App.SearchChannelsUserNotIn(c.TeamId, c.Session.UserId, props.Term); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func autocompleteChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
term := r.URL.Query().Get("term")
|
||||
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if channels, err := c.App.SearchChannels(c.TeamId, term); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
view := model.ChannelViewFromJson(r.Body)
|
||||
if view == nil {
|
||||
c.SetInvalidParam("viewChannel", "channel_view")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.ViewChannel(view, c.Session.UserId, !c.Session.IsMobileApp()); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func getChannelMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
|
||||
userIds := model.ArrayFromJson(r.Body)
|
||||
if len(userIds) == 0 {
|
||||
c.SetInvalidParam("getChannelMembersByIds", "user_ids")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if members, err := c.App.GetChannelMembersByIds(channelId, userIds); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(members.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannelMemberRoles(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
channelId := params["channel_id"]
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
userId := props["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("updateChannelMemberRoles", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_MANAGE_CHANNEL_ROLES) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_CHANNEL_ROLES)
|
||||
return
|
||||
}
|
||||
|
||||
newRoles := props["new_roles"]
|
||||
if !(model.IsValidUserRoles(newRoles)) {
|
||||
c.SetInvalidParam("updateChannelMemberRoles", "new_roles")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.UpdateChannelMemberRoles(channelId, userId, newRoles); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
2007
api/channel_test.go
2007
api/channel_test.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
286
api/command.go
286
api/command.go
@@ -1,286 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitCommand() {
|
||||
api.BaseRoutes.Commands.Handle("/execute", api.ApiUserRequired(executeCommand)).Methods("POST")
|
||||
api.BaseRoutes.Commands.Handle("/list", api.ApiUserRequired(listCommands)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Commands.Handle("/create", api.ApiUserRequired(createCommand)).Methods("POST")
|
||||
api.BaseRoutes.Commands.Handle("/update", api.ApiUserRequired(updateCommand)).Methods("POST")
|
||||
api.BaseRoutes.Commands.Handle("/list_team_commands", api.ApiUserRequired(listTeamCommands)).Methods("GET")
|
||||
api.BaseRoutes.Commands.Handle("/regen_token", api.ApiUserRequired(regenCommandToken)).Methods("POST")
|
||||
api.BaseRoutes.Commands.Handle("/delete", api.ApiUserRequired(deleteCommand)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.Teams.Handle("/command_test", api.ApiAppHandler(testCommand)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/command_test", api.ApiAppHandler(testCommand)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/command_test_e", api.ApiAppHandler(testEphemeralCommand)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/command_test_e", api.ApiAppHandler(testEphemeralCommand)).Methods("GET")
|
||||
}
|
||||
|
||||
func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
commands, err := c.App.ListAutocompleteCommands(c.TeamId, c.T)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.CommandListToJson(commands)))
|
||||
}
|
||||
|
||||
func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
commandArgs := model.CommandArgsFromJson(r.Body)
|
||||
if commandArgs == nil {
|
||||
c.SetInvalidParam("executeCommand", "command_args")
|
||||
return
|
||||
}
|
||||
|
||||
if len(commandArgs.Command) <= 1 || strings.Index(commandArgs.Command, "/") != 0 {
|
||||
c.Err = model.NewAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(commandArgs.ChannelId) > 0 {
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, commandArgs.ChannelId, model.PERMISSION_USE_SLASH_COMMANDS) {
|
||||
c.SetPermissionError(model.PERMISSION_USE_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
commandArgs.TeamId = c.TeamId
|
||||
commandArgs.UserId = c.Session.UserId
|
||||
commandArgs.T = c.T
|
||||
commandArgs.Session = c.Session
|
||||
commandArgs.SiteURL = c.GetSiteURLHeader()
|
||||
|
||||
response, err := c.App.ExecuteCommand(commandArgs)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(response.ToJson()))
|
||||
}
|
||||
|
||||
func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
cmd := model.CommandFromJson(r.Body)
|
||||
|
||||
if cmd == nil {
|
||||
c.SetInvalidParam("createCommand", "command")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.CreatorId = c.Session.UserId
|
||||
cmd.TeamId = c.TeamId
|
||||
|
||||
rcmd, err := c.App.CreateCommand(cmd)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(rcmd.ToJson()))
|
||||
}
|
||||
|
||||
func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
cmd := model.CommandFromJson(r.Body)
|
||||
|
||||
if cmd == nil {
|
||||
c.SetInvalidParam("updateCommand", "command")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
oldCmd, err := c.App.GetCommand(cmd.Id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.TeamId != oldCmd.TeamId {
|
||||
c.Err = model.NewAppError("updateCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, oldCmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != oldCmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
rcmd, err := c.App.UpdateCommand(oldCmd, cmd)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
|
||||
w.Write([]byte(rcmd.ToJson()))
|
||||
}
|
||||
|
||||
func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
cmds, err := c.App.ListTeamCommands(c.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.CommandListToJson(cmds)))
|
||||
}
|
||||
|
||||
func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("regenCommandToken", "id")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
cmd, err := c.App.GetCommand(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.TeamId != cmd.TeamId {
|
||||
c.Err = model.NewAppError("regenCommandToken", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
|
||||
return
|
||||
}
|
||||
|
||||
rcmd, err := c.App.RegenCommandToken(cmd)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(rcmd.ToJson()))
|
||||
}
|
||||
|
||||
func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteCommand", "id")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
cmd, err := c.App.GetCommand(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.TeamId != cmd.TeamId {
|
||||
c.Err = model.NewAppError("deleteCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
return
|
||||
}
|
||||
|
||||
err = c.App.DeleteCommand(cmd.Id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
|
||||
func testCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
msg := ""
|
||||
if r.Method == "POST" {
|
||||
msg = msg + "\ntoken=" + r.FormValue("token")
|
||||
msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
msg = string(body)
|
||||
}
|
||||
|
||||
rc := &model.CommandResponse{
|
||||
Text: "test command response " + msg,
|
||||
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
|
||||
}
|
||||
|
||||
w.Write([]byte(rc.ToJson()))
|
||||
}
|
||||
|
||||
func testEphemeralCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
msg := ""
|
||||
if r.Method == "POST" {
|
||||
msg = msg + "\ntoken=" + r.FormValue("token")
|
||||
msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
msg = string(body)
|
||||
}
|
||||
|
||||
rc := &model.CommandResponse{
|
||||
Text: "test command response " + msg,
|
||||
ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL,
|
||||
}
|
||||
|
||||
w.Write([]byte(rc.ToJson()))
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestEchoCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel1 := th.BasicChannel
|
||||
|
||||
echoTestString := "/echo test"
|
||||
|
||||
if r1 := Client.Must(Client.Command(channel1.Id, echoTestString)).Data.(*model.CommandResponse); r1 == nil {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
if r1 := Client.Must(Client.Command(channel1.Id, "/echo ")).Data.(*model.CommandResponse); r1 == nil {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Echo command failed to send")
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestExpandCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, "/expand")).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPreference(model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSE_SETTING)).Data.(*model.Preference)
|
||||
if p1.Value != "false" {
|
||||
t.Fatal("preference not updated correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollapseCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, "/collapse")).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPreference(model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSE_SETTING)).Data.(*model.Preference)
|
||||
if p1.Value != "true" {
|
||||
t.Fatal("preference not updated correctly")
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestGroupmsgCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
team := th.BasicTeam
|
||||
user1 := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
user3 := th.CreateUser(th.BasicClient)
|
||||
user4 := th.CreateUser(th.BasicClient)
|
||||
user5 := th.CreateUser(th.BasicClient)
|
||||
user6 := th.CreateUser(th.BasicClient)
|
||||
user7 := th.CreateUser(th.BasicClient)
|
||||
user8 := th.CreateUser(th.BasicClient)
|
||||
user9 := th.CreateUser(th.BasicClient)
|
||||
th.LinkUserToTeam(user3, team)
|
||||
th.LinkUserToTeam(user4, team)
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/groupmsg "+user2.Username+","+user3.Username)).Data.(*model.CommandResponse)
|
||||
|
||||
group1 := model.GetGroupNameFromUserIds([]string{user1.Id, user2.Id, user3.Id})
|
||||
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+group1) {
|
||||
t.Fatal("failed to create group channel")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/groupmsg "+user3.Username+","+user4.Username+" foobar")).Data.(*model.CommandResponse)
|
||||
group2 := model.GetGroupNameFromUserIds([]string{user1.Id, user3.Id, user4.Id})
|
||||
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+group2) {
|
||||
t.Fatal("failed to create second direct channel")
|
||||
}
|
||||
if result := Client.Must(Client.SearchPosts("foobar", false)).Data.(*model.PostList); len(result.Order) == 0 {
|
||||
t.Fatal("post did not get sent to direct message")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/groupmsg "+user2.Username+","+user3.Username)).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+group1) {
|
||||
t.Fatal("failed to go back to existing group channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.Command("", "/groupmsg "+user2.Username+" foobar"))
|
||||
Client.Must(Client.Command("", "/groupmsg "+user2.Username+","+user3.Username+","+user4.Username+","+user5.Username+","+user6.Username+","+user7.Username+","+user8.Username+","+user9.Username+" foobar"))
|
||||
Client.Must(Client.Command("", "/groupmsg junk foobar"))
|
||||
Client.Must(Client.Command("", "/groupmsg junk,junk2 foobar"))
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestHelpCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
HelpLink := *th.App.Config().SupportSettings.HelpLink
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.SupportSettings.HelpLink = HelpLink })
|
||||
}()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.SupportSettings.HelpLink = "" })
|
||||
rs1 := Client.Must(Client.Command(channel.Id, "/help ")).Data.(*model.CommandResponse)
|
||||
if rs1.GotoLocation != model.SUPPORT_SETTINGS_DEFAULT_HELP_LINK {
|
||||
t.Fatal("failed to default help link")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.SupportSettings.HelpLink = "https://docs.mattermost.com/guides/user.html"
|
||||
})
|
||||
rs2 := Client.Must(Client.Command(channel.Id, "/help ")).Data.(*model.CommandResponse)
|
||||
if rs2.GotoLocation != "https://docs.mattermost.com/guides/user.html" {
|
||||
t.Fatal("failed to help link")
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestInvitePeopleCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, "/invite_people test@example.com")).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
r2 := Client.Must(Client.Command(channel.Id, "/invite_people test1@example.com test2@example.com")).Data.(*model.CommandResponse)
|
||||
if r2 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
r3 := Client.Must(Client.Command(channel.Id, "/invite_people")).Data.(*model.CommandResponse)
|
||||
if r3 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
// also used to test /open (see command_open_test.go)
|
||||
func testJoinCommands(t *testing.T, alias string) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
team := th.BasicTeam
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel0 := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel0 = Client.Must(Client.CreateChannel(channel0)).Data.(*model.Channel)
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel1.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel)
|
||||
|
||||
rs5 := Client.Must(Client.Command(channel0.Id, "/"+alias+" "+channel2.Name)).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
|
||||
t.Fatal("failed to join channel")
|
||||
}
|
||||
|
||||
rs6 := Client.Must(Client.Command(channel0.Id, "/"+alias+" "+channel3.Name)).Data.(*model.CommandResponse)
|
||||
if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
|
||||
t.Fatal("should not have joined direct message channel")
|
||||
}
|
||||
|
||||
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
|
||||
found := false
|
||||
for _, c := range *c1 {
|
||||
if c.Id == channel2.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("did not join channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinCommands(t *testing.T) {
|
||||
testJoinCommands(t, "join")
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestLeaveCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
team := th.BasicTeam
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
Client.Must(Client.JoinChannel(channel1.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
Client.Must(Client.JoinChannel(channel2.Id))
|
||||
Client.Must(Client.AddChannelMember(channel2.Id, user2.Id))
|
||||
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel)
|
||||
|
||||
rs1 := Client.Must(Client.Command(channel1.Id, "/leave")).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+model.DEFAULT_CHANNEL) {
|
||||
t.Fatal("failed to leave open channel 1")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command(channel2.Id, "/leave")).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+model.DEFAULT_CHANNEL) {
|
||||
t.Fatal("failed to leave private channel 1")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command(channel3.Id, "/leave")).Data.(*model.CommandResponse)
|
||||
if strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+model.DEFAULT_CHANNEL) {
|
||||
t.Fatal("should not have left direct message channel")
|
||||
}
|
||||
|
||||
cdata := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
|
||||
found := false
|
||||
for _, c := range *cdata {
|
||||
if c.Id == channel1.Id || c.Id == channel2.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("did not leave right channels")
|
||||
}
|
||||
|
||||
for _, c := range *cdata {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
if _, err := Client.LeaveChannel(c.Id); err == nil {
|
||||
t.Fatal("should have errored on leaving default channel")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestLoadTestHelpCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/test help")).Data.(*model.CommandResponse)
|
||||
if !strings.Contains(rs.Text, "Mattermost testing commands to help") {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestSetupCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/test setup fuzz 1 1 1")).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Created environment" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestUsersCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/test users fuzz 1 2")).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Added users" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestChannelsCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/test channels fuzz 1 2")).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Added channels" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestPostsCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/test posts fuzz 2 3 2")).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Added posts" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogoutTestCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/logout"))
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestMeCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
testString := "/me hello"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, testString)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `*hello*` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug response")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestMsgCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
team := th.BasicTeam
|
||||
user1 := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
user3 := th.CreateUser(th.BasicClient)
|
||||
th.LinkUserToTeam(user3, team)
|
||||
|
||||
Client.Must(Client.CreateDirectChannel(user2.Id))
|
||||
Client.Must(Client.CreateDirectChannel(user3.Id))
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/msg "+user2.Username)).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to create direct channel")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/msg "+user3.Username+" foobar")).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user3.Id) && !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user3.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to create second direct channel")
|
||||
}
|
||||
if result := Client.Must(Client.SearchPosts("foobar", false)).Data.(*model.PostList); len(result.Order) == 0 {
|
||||
t.Fatalf("post did not get sent to direct message")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/msg "+user2.Username)).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to go back to existing direct channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.Command("", "/msg "+th.BasicUser.Username+" foobar"))
|
||||
Client.Must(Client.Command("", "/msg junk foobar"))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenCommands(t *testing.T) {
|
||||
testJoinCommands(t, "open")
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSearchCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/search"))
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSettingsCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/settings"))
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShortcutsCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/shortcuts"))
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestShrugCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
testString := "/shrug"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, testString)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `¯\\\_(ツ)\_/¯` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug response")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestStatusCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
commandAndTest(t, th, "away")
|
||||
commandAndTest(t, th, "offline")
|
||||
commandAndTest(t, th, "online")
|
||||
}
|
||||
|
||||
func commandAndTest(t *testing.T, th *TestHelper, status string) {
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
user := th.BasicUser
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, "/"+status)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
statuses := Client.Must(Client.GetStatuses()).Data.(map[string]string)
|
||||
|
||||
if status == "offline" {
|
||||
status = ""
|
||||
}
|
||||
if statuses[user.Id] != status {
|
||||
t.Fatal("Error setting status " + status)
|
||||
}
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestListCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
if results, err := Client.ListCommands(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
commands := results.Data.([]*model.Command)
|
||||
foundEcho := false
|
||||
|
||||
for _, command := range commands {
|
||||
if command.Trigger == "echo" {
|
||||
foundEcho = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundEcho {
|
||||
t.Fatal("Couldn't find echo command")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCommand(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user := th.SystemAdminUser
|
||||
team := th.SystemAdminTeam
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
|
||||
cmd1 := &model.Command{
|
||||
CreatorId: user.Id,
|
||||
TeamId: team.Id,
|
||||
URL: "http://nowhere.com",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "trigger"}
|
||||
|
||||
if _, err := Client.CreateCommand(cmd1); err == nil {
|
||||
t.Fatal("should have failed because not admin")
|
||||
}
|
||||
|
||||
Client = th.SystemAdminClient
|
||||
|
||||
cmd2 := &model.Command{
|
||||
CreatorId: user.Id,
|
||||
TeamId: team.Id,
|
||||
URL: "http://nowhere.com",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "trigger"}
|
||||
|
||||
var rcmd *model.Command
|
||||
if result, err := Client.CreateCommand(cmd2); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rcmd = result.Data.(*model.Command)
|
||||
}
|
||||
|
||||
if rcmd.CreatorId != user.Id {
|
||||
t.Fatal("user ids didn't match")
|
||||
}
|
||||
|
||||
if rcmd.TeamId != team.Id {
|
||||
t.Fatal("team ids didn't match")
|
||||
}
|
||||
|
||||
cmd3 := &model.Command{
|
||||
CreatorId: "123",
|
||||
TeamId: "456",
|
||||
URL: "http://nowhere.com",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "trigger"}
|
||||
if _, err := Client.CreateCommand(cmd3); err == nil {
|
||||
t.Fatal("trigger cannot be duplicated")
|
||||
}
|
||||
|
||||
cmd4 := cmd3
|
||||
if _, err := Client.CreateCommand(cmd4); err == nil {
|
||||
t.Fatal("command cannot be duplicated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListTeamCommands(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
|
||||
cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"}
|
||||
cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
|
||||
|
||||
if result, err := Client.ListTeamCommands(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
cmds := result.Data.([]*model.Command)
|
||||
|
||||
if len(cmds) != 1 {
|
||||
t.Fatal("incorrect number of cmd")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCommand(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
user := th.SystemAdminUser
|
||||
team := th.SystemAdminTeam
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
|
||||
cmd1 := &model.Command{
|
||||
CreatorId: user.Id,
|
||||
TeamId: team.Id,
|
||||
URL: "http://nowhere.com",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "trigger"}
|
||||
|
||||
cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
|
||||
|
||||
cmd2 := &model.Command{
|
||||
CreatorId: user.Id,
|
||||
TeamId: team.Id,
|
||||
URL: "http://nowhere.com",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "trigger2",
|
||||
Token: cmd1.Token,
|
||||
Id: cmd1.Id}
|
||||
|
||||
if result, err := Client.UpdateCommand(cmd2); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.Command).Trigger == cmd1.Trigger {
|
||||
t.Fatal("update didn't work properly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegenToken(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
|
||||
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"}
|
||||
cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["id"] = cmd.Id
|
||||
|
||||
if result, err := Client.RegenCommandToken(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.Command).Token == cmd.Token {
|
||||
t.Fatal("regen didn't work properly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCommand(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = enableCommands })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
|
||||
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"}
|
||||
cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["id"] = cmd.Id
|
||||
|
||||
if _, err := Client.DeleteCommand(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmds := Client.Must(Client.ListTeamCommands()).Data.([]*model.Command)
|
||||
if len(cmds) != 0 {
|
||||
t.Fatal("delete didn't work properly")
|
||||
}
|
||||
|
||||
cmd2 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger2"}
|
||||
cmd2 = Client.Must(Client.CreateCommand(cmd2)).Data.(*model.Command)
|
||||
|
||||
data2 := make(map[string]string)
|
||||
data2["id"] = cmd2.Id
|
||||
if _, err := th.BasicClient.DeleteCommand(data2); err == nil {
|
||||
t.Fatal("Should have errored. Your not allowed to delete other's commands")
|
||||
}
|
||||
|
||||
cmds2 := Client.Must(Client.ListTeamCommands()).Data.([]*model.Command)
|
||||
if len(cmds2) != 1 {
|
||||
t.Fatal("Client was able to delete command without permission.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestCommand(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
channel1 := th.SystemAdminChannel
|
||||
|
||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||
allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
|
||||
})
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost" })
|
||||
|
||||
cmd1 := &model.Command{
|
||||
URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V3 + "/teams/command_test",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "testcommand",
|
||||
}
|
||||
|
||||
cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, "/testcommand")).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Test command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
|
||||
// including system 'joined' message
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Test command response failed to send")
|
||||
}
|
||||
|
||||
cmdPosted := false
|
||||
for _, post := range p1.Posts {
|
||||
if strings.Contains(post.Message, "test command response") {
|
||||
cmdPosted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !cmdPosted {
|
||||
t.Fatal("Test command response failed to post")
|
||||
}
|
||||
|
||||
cmd2 := &model.Command{
|
||||
URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V3 + "/teams/command_test",
|
||||
Method: model.COMMAND_METHOD_GET,
|
||||
Trigger: "test2",
|
||||
}
|
||||
|
||||
cmd2 = Client.Must(Client.CreateCommand(cmd2)).Data.(*model.Command)
|
||||
|
||||
r2 := Client.Must(Client.Command(channel1.Id, "/test2")).Data.(*model.CommandResponse)
|
||||
if r2 == nil {
|
||||
t.Fatal("Test2 command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
|
||||
if len(p2.Order) != 3 {
|
||||
t.Fatal("Test command failed to send")
|
||||
}
|
||||
}
|
||||
451
api/context.go
451
api/context.go
@@ -1,451 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
goi18n "github.com/nicksnyder/go-i18n/i18n"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
App *app.App
|
||||
Session model.Session
|
||||
RequestId string
|
||||
IpAddress string
|
||||
Path string
|
||||
Err *model.AppError
|
||||
siteURLHeader string
|
||||
teamURLValid bool
|
||||
teamURL string
|
||||
T goi18n.TranslateFunc
|
||||
Locale string
|
||||
TeamId string
|
||||
}
|
||||
|
||||
func (api *API) ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, true, false, false, false, false}
|
||||
}
|
||||
|
||||
func (api *API) AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, false, false, false, false, false}
|
||||
}
|
||||
|
||||
func (api *API) AppHandlerIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, false, false, true, false, false}
|
||||
}
|
||||
|
||||
func (api *API) ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, false, true, false, false, false, true}
|
||||
}
|
||||
|
||||
func (api *API) ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler {
|
||||
return &handler{api.App, h, true, false, true, isUserActivity, false, false, true}
|
||||
}
|
||||
|
||||
func (api *API) ApiUserRequiredMfa(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, false, true, false, false, false, false}
|
||||
}
|
||||
|
||||
func (api *API) UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, false, false, false, false, false, true}
|
||||
}
|
||||
|
||||
func (api *API) AppHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, false, false, false, true, false}
|
||||
}
|
||||
|
||||
func (api *API) ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, true, true, false, false, false, true}
|
||||
}
|
||||
|
||||
func (api *API) ApiAdminSystemRequiredTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, true, true, false, false, true, true}
|
||||
}
|
||||
|
||||
func (api *API) ApiAppHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, true, false, false, true, false}
|
||||
}
|
||||
|
||||
func (api *API) ApiUserRequiredTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, true, false, true, false, false, true, true}
|
||||
}
|
||||
|
||||
func (api *API) ApiAppHandlerTrustRequesterIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &handler{api.App, h, false, false, true, false, true, true, false}
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
app *app.App
|
||||
handleFunc func(*Context, http.ResponseWriter, *http.Request)
|
||||
requireUser bool
|
||||
requireSystemAdmin bool
|
||||
isApi bool
|
||||
isUserActivity bool
|
||||
isTeamIndependent bool
|
||||
trustRequester bool
|
||||
requireMfa bool
|
||||
}
|
||||
|
||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
now := time.Now()
|
||||
mlog.Debug(fmt.Sprintf("%v", r.URL.Path))
|
||||
|
||||
c := &Context{}
|
||||
c.App = h.app
|
||||
c.T, c.Locale = utils.GetTranslationsAndLocale(w, r)
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = utils.GetIpAddress(r)
|
||||
c.TeamId = mux.Vars(r)["team_id"]
|
||||
|
||||
if metrics := c.App.Metrics; metrics != nil && h.isApi {
|
||||
metrics.IncrementHttpRequest()
|
||||
}
|
||||
|
||||
token, tokenLocation := app.ParseAuthTokenFromRequest(r)
|
||||
|
||||
// CSRF Check
|
||||
if tokenLocation == app.TokenLocationCookie && (h.requireSystemAdmin || h.requireUser) && !h.trustRequester {
|
||||
if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML {
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized)
|
||||
token = ""
|
||||
}
|
||||
}
|
||||
|
||||
c.SetSiteURLHeader(app.GetProtocol(r) + "://" + r.Host)
|
||||
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
|
||||
w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.License() != nil))
|
||||
|
||||
// Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking
|
||||
if !h.isApi {
|
||||
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||
w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'")
|
||||
} else {
|
||||
// All api response bodies will be JSON formatted by default
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if r.Method == "GET" {
|
||||
w.Header().Set("Expires", "0")
|
||||
}
|
||||
}
|
||||
|
||||
if len(token) != 0 {
|
||||
session, err := c.App.GetSession(token)
|
||||
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprintf("Invalid session err=%v", err.Error()))
|
||||
c.RemoveSessionCookie(w, r)
|
||||
if h.requireUser || h.requireSystemAdmin {
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized)
|
||||
}
|
||||
} else if !session.IsOAuth && tokenLocation == app.TokenLocationQueryString {
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized)
|
||||
} else {
|
||||
c.Session = *session
|
||||
}
|
||||
|
||||
// Rate limit by UserID
|
||||
if c.App.Srv.RateLimiter != nil && c.App.Srv.RateLimiter.UserIdRateLimit(c.Session.UserId, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if h.isApi || h.isTeamIndependent {
|
||||
c.setTeamURL(c.GetSiteURLHeader(), false)
|
||||
c.Path = r.URL.Path
|
||||
} else {
|
||||
splitURL := strings.Split(r.URL.Path, "/")
|
||||
c.setTeamURL(c.GetSiteURLHeader()+"/"+splitURL[1], true)
|
||||
c.Path = "/" + strings.Join(splitURL[2:], "/")
|
||||
}
|
||||
|
||||
if h.isApi && !*c.App.Config().ServiceSettings.EnableAPIv3 {
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.v3_disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if c.Err == nil && h.requireUser {
|
||||
c.UserRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.requireMfa {
|
||||
c.MfaRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.requireSystemAdmin {
|
||||
c.SystemAdminRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 {
|
||||
c.App.SetStatusOnline(c.Session.UserId, c.Session.Id, false)
|
||||
c.App.UpdateLastActivityAtIfNeeded(c.Session)
|
||||
}
|
||||
|
||||
if c.Err == nil && (h.requireUser || h.requireSystemAdmin) {
|
||||
//check if teamId exist
|
||||
c.CheckTeamId()
|
||||
}
|
||||
|
||||
if h.isApi {
|
||||
atomic.StoreInt32(model.UsedApiV3, 1)
|
||||
}
|
||||
|
||||
if c.Err == nil {
|
||||
h.handleFunc(c, w, r)
|
||||
}
|
||||
|
||||
// Handle errors that have occoured
|
||||
if c.Err != nil {
|
||||
c.Err.Translate(c.T)
|
||||
c.Err.RequestId = c.RequestId
|
||||
c.LogError(c.Err)
|
||||
c.Err.Where = r.URL.Path
|
||||
|
||||
// Block out detailed error when not in developer mode
|
||||
if !*c.App.Config().ServiceSettings.EnableDeveloper {
|
||||
c.Err.DetailedError = ""
|
||||
}
|
||||
|
||||
if h.isApi {
|
||||
w.WriteHeader(c.Err.StatusCode)
|
||||
w.Write([]byte(c.Err.ToJson()))
|
||||
|
||||
if c.App.Metrics != nil {
|
||||
c.App.Metrics.IncrementHttpError()
|
||||
}
|
||||
} else {
|
||||
if c.Err.StatusCode == http.StatusUnauthorized {
|
||||
http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if h.isApi && c.App.Metrics != nil {
|
||||
if r.URL.Path != model.API_URL_SUFFIX_V3+"/users/websocket" {
|
||||
elapsed := float64(time.Since(now)) / float64(time.Second)
|
||||
c.App.Metrics.ObserveHttpRequestDuration(elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) LogAudit(extraInfo string) {
|
||||
audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id}
|
||||
if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil {
|
||||
c.LogError(r.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
|
||||
|
||||
if len(c.Session.UserId) > 0 {
|
||||
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId)
|
||||
}
|
||||
|
||||
audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id}
|
||||
if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil {
|
||||
c.LogError(r.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) LogError(err *model.AppError) {
|
||||
|
||||
// filter out endless reconnects
|
||||
if c.Path == "/api/v3/users/websocket" && err.StatusCode == 401 || err.Id == "web.check_browser_compatibility.app_error" {
|
||||
c.LogDebug(err)
|
||||
} else if err.Id != "api.post.create_post.town_square_read_only" {
|
||||
mlog.Error(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
|
||||
c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) LogDebug(err *model.AppError) {
|
||||
mlog.Debug(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
|
||||
c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
|
||||
}
|
||||
|
||||
func (c *Context) UserRequired() {
|
||||
if !*c.App.Config().ServiceSettings.EnableUserAccessTokens && c.Session.Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserAccessToken", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if len(c.Session.UserId) == 0 {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) MfaRequired() {
|
||||
// Must be licensed for MFA and have it configured for enforcement
|
||||
if license := c.App.License(); license == nil || !*license.Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication {
|
||||
return
|
||||
}
|
||||
|
||||
// OAuth integrations are excepted
|
||||
if c.Session.IsOAuth {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-c.App.Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "MfaRequired", http.StatusUnauthorized)
|
||||
return
|
||||
} else {
|
||||
user := result.Data.(*model.User)
|
||||
|
||||
// Only required for email and ldap accounts
|
||||
if user.AuthService != "" &&
|
||||
user.AuthService != model.USER_AUTH_SERVICE_EMAIL &&
|
||||
user.AuthService != model.USER_AUTH_SERVICE_LDAP {
|
||||
return
|
||||
}
|
||||
|
||||
if !user.MfaActive {
|
||||
c.Err = model.NewAppError("", "api.context.mfa_required.app_error", nil, "MfaRequired", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SystemAdminRequired() {
|
||||
if len(c.Session.UserId) == 0 {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "SystemAdminRequired", http.StatusUnauthorized)
|
||||
return
|
||||
} else if !c.IsSystemAdmin() {
|
||||
c.Err = model.NewAppError("", "api.context.permissions.app_error", nil, "AdminRequired", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) IsSystemAdmin() bool {
|
||||
return c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM)
|
||||
}
|
||||
|
||||
func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
|
||||
cookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_TOKEN,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
userCookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_USER,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
}
|
||||
|
||||
http.SetCookie(w, cookie)
|
||||
http.SetCookie(w, userCookie)
|
||||
}
|
||||
|
||||
func (c *Context) SetInvalidParam(where string, name string) {
|
||||
c.Err = NewInvalidParamError(where, name)
|
||||
}
|
||||
|
||||
func NewInvalidParamError(where string, name string) *model.AppError {
|
||||
err := model.NewAppError(where, "api.context.invalid_param.app_error", map[string]interface{}{"Name": name}, "", http.StatusBadRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Context) SetPermissionError(permission *model.Permission) {
|
||||
c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", "+"permission="+permission.Id, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func (c *Context) setTeamURL(url string, valid bool) {
|
||||
c.teamURL = url
|
||||
c.teamURLValid = valid
|
||||
}
|
||||
|
||||
func (c *Context) SetTeamURLFromSession() {
|
||||
if result := <-c.App.Srv.Store.Team().Get(c.TeamId); result.Err == nil {
|
||||
c.setTeamURL(c.GetSiteURLHeader()+"/"+result.Data.(*model.Team).Name, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SetSiteURLHeader(url string) {
|
||||
c.siteURLHeader = strings.TrimRight(url, "/")
|
||||
}
|
||||
|
||||
func (c *Context) GetTeamURL() string {
|
||||
if !c.teamURLValid {
|
||||
c.SetTeamURLFromSession()
|
||||
if !c.teamURLValid {
|
||||
mlog.Debug("Team URL accessed when not valid. Team URL should not be used in API functions or those that are team independent")
|
||||
}
|
||||
}
|
||||
return c.teamURL
|
||||
}
|
||||
|
||||
func (c *Context) GetSiteURLHeader() string {
|
||||
return c.siteURLHeader
|
||||
}
|
||||
|
||||
func (c *Context) HandleEtag(etag string, routeName string, w http.ResponseWriter, r *http.Request) bool {
|
||||
metrics := c.App.Metrics
|
||||
if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 {
|
||||
if et == etag {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
if metrics != nil {
|
||||
metrics.IncrementEtagHitCounter(routeName)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if metrics != nil {
|
||||
metrics.IncrementEtagMissCounter(routeName)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsApiCall(r *http.Request) bool {
|
||||
return strings.Index(r.URL.Path, "/api/") == 0
|
||||
}
|
||||
|
||||
func Handle404(a *app.App, w http.ResponseWriter, r *http.Request) {
|
||||
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
|
||||
|
||||
mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)))
|
||||
|
||||
if IsApiCall(r) {
|
||||
w.WriteHeader(err.StatusCode)
|
||||
err.DetailedError = "There doesn't appear to be an api call for the url='" + r.URL.Path + "'. Typo? are you missing a team_id or user_id as part of the url?"
|
||||
w.Write([]byte(err.ToJson()))
|
||||
} else {
|
||||
utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) CheckTeamId() {
|
||||
if c.TeamId != "" && c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
if result := <-c.App.Srv.Store.Team().Get(c.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSiteURLHeader(t *testing.T) {
|
||||
c := &Context{}
|
||||
|
||||
testCases := []struct {
|
||||
url string
|
||||
want string
|
||||
}{
|
||||
{"http://mattermost.com/", "http://mattermost.com"},
|
||||
{"http://mattermost.com", "http://mattermost.com"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
c.SetSiteURLHeader(tc.url)
|
||||
|
||||
if c.siteURLHeader != tc.want {
|
||||
t.Fatalf("expected %s, got %s", tc.want, c.siteURLHeader)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
228
api/emoji.go
228
api/emoji.go
@@ -1,228 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/gif"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"image/color/palette"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitEmoji() {
|
||||
api.BaseRoutes.Emoji.Handle("/list", api.ApiUserRequired(getEmoji)).Methods("GET")
|
||||
api.BaseRoutes.Emoji.Handle("/create", api.ApiUserRequired(createEmoji)).Methods("POST")
|
||||
api.BaseRoutes.Emoji.Handle("/delete", api.ApiUserRequired(deleteEmoji)).Methods("POST")
|
||||
api.BaseRoutes.Emoji.Handle("/{id:[A-Za-z0-9_]+}", api.ApiUserRequiredTrustRequester(getEmojiImage)).Methods("GET")
|
||||
}
|
||||
|
||||
func getEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewAppError("getEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
listEmoji, err := c.App.GetEmojiList(0, 100000, "")
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.EmojiListToJson(listEmoji)))
|
||||
}
|
||||
}
|
||||
|
||||
func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if emojiInterface := c.App.Emoji; emojiInterface != nil &&
|
||||
!emojiInterface.CanUserCreateEmoji(c.Session.Roles, c.Session.TeamMembers) {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.create.permissions.app_error", nil, "user_id="+c.Session.UserId, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if r.ContentLength > app.MaxEmojiFileSize {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.create.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(app.MaxEmojiFileSize); err != nil {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.create.parse.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
props := m.Value
|
||||
|
||||
emoji := model.EmojiFromJson(strings.NewReader(props["emoji"][0]))
|
||||
if emoji == nil {
|
||||
c.SetInvalidParam("createEmoji", "emoji")
|
||||
return
|
||||
}
|
||||
|
||||
// wipe the emoji id so that existing emojis can't get overwritten
|
||||
emoji.Id = ""
|
||||
|
||||
// do our best to validate the emoji before committing anything to the DB so that we don't have to clean up
|
||||
// orphaned files left over when validation fails later on
|
||||
emoji.PreSave()
|
||||
if err := emoji.IsValid(); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
if emoji.CreatorId != c.Session.UserId {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.create.other_user.app_error", nil, "", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-c.App.Srv.Store.Emoji().GetByName(emoji.Name); result.Err == nil && result.Data != nil {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if imageData := m.File["image"]; len(imageData) == 0 {
|
||||
c.SetInvalidParam("createEmoji", "image")
|
||||
return
|
||||
} else if err := c.App.UploadEmojiImage(emoji.Id, imageData[0]); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-c.App.Srv.Store.Emoji().Save(emoji); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EMOJI_ADDED, "", "", "", nil)
|
||||
message.Add("emoji", result.Data.(*model.Emoji).ToJson())
|
||||
|
||||
c.App.Publish(message)
|
||||
w.Write([]byte(result.Data.(*model.Emoji).ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewAppError("deleteEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
c.Err = model.NewAppError("deleteImage", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteEmoji", "id")
|
||||
return
|
||||
}
|
||||
|
||||
emoji, err := c.App.GetEmoji(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != emoji.CreatorId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.Err = model.NewAppError("deleteEmoji", "api.emoji.delete.permissions.app_error", nil, "user_id="+c.Session.UserId, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err = c.App.DeleteEmoji(emoji)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
}
|
||||
|
||||
func getEmojiImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().ServiceSettings.EnableCustomEmoji {
|
||||
c.Err = model.NewAppError("getEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
c.Err = model.NewAppError("getEmojiImage", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("getEmojiImage", "id")
|
||||
return
|
||||
}
|
||||
|
||||
image, imageType, err := c.App.GetEmojiImage(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "image/"+imageType)
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
w.Write(image)
|
||||
}
|
||||
|
||||
func resizeEmoji(img image.Image, width int, height int) image.Image {
|
||||
emojiWidth := float64(width)
|
||||
emojiHeight := float64(height)
|
||||
|
||||
var emoji image.Image
|
||||
if emojiHeight <= app.MaxEmojiHeight && emojiWidth <= app.MaxEmojiWidth {
|
||||
emoji = img
|
||||
} else {
|
||||
emoji = imaging.Fit(img, app.MaxEmojiWidth, app.MaxEmojiHeight, imaging.Lanczos)
|
||||
}
|
||||
return emoji
|
||||
}
|
||||
|
||||
func resizeEmojiGif(gifImg *gif.GIF) *gif.GIF {
|
||||
// Create a new RGBA image to hold the incremental frames.
|
||||
firstFrame := gifImg.Image[0].Bounds()
|
||||
b := image.Rect(0, 0, firstFrame.Dx(), firstFrame.Dy())
|
||||
img := image.NewRGBA(b)
|
||||
|
||||
resizedImage := image.Image(nil)
|
||||
// Resize each frame.
|
||||
for index, frame := range gifImg.Image {
|
||||
bounds := frame.Bounds()
|
||||
draw.Draw(img, bounds, frame, bounds.Min, draw.Over)
|
||||
resizedImage = resizeEmoji(img, firstFrame.Dx(), firstFrame.Dy())
|
||||
gifImg.Image[index] = imageToPaletted(resizedImage)
|
||||
}
|
||||
// Set new gif width and height
|
||||
gifImg.Config.Width = resizedImage.Bounds().Dx()
|
||||
gifImg.Config.Height = resizedImage.Bounds().Dy()
|
||||
return gifImg
|
||||
}
|
||||
|
||||
func imageToPaletted(img image.Image) *image.Paletted {
|
||||
b := img.Bounds()
|
||||
pm := image.NewPaletted(b, palette.Plan9)
|
||||
draw.FloydSteinberg.Draw(pm, b, img, image.ZP)
|
||||
return pm
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/gif"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestGetEmoji(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
EnableCustomEmoji := *th.App.Config().ServiceSettings.EnableCustomEmoji
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
|
||||
emojis := []*model.Emoji{
|
||||
{
|
||||
CreatorId: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
CreatorId: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
CreatorId: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
},
|
||||
}
|
||||
|
||||
for i, emoji := range emojis {
|
||||
emojis[i] = store.Must(th.App.Srv.Store.Emoji().Save(emoji)).(*model.Emoji)
|
||||
}
|
||||
defer func() {
|
||||
for _, emoji := range emojis {
|
||||
store.Must(th.App.Srv.Store.Emoji().Delete(emoji.Id, time.Now().Unix()))
|
||||
}
|
||||
}()
|
||||
|
||||
if returnedEmojis, err := Client.ListEmoji(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
for _, emoji := range emojis {
|
||||
found := false
|
||||
|
||||
for _, savedEmoji := range returnedEmojis {
|
||||
if emoji.Id == savedEmoji.Id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("failed to get emoji with id %v", emoji.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleted := &model.Emoji{
|
||||
CreatorId: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
DeleteAt: 1,
|
||||
}
|
||||
deleted = store.Must(th.App.Srv.Store.Emoji().Save(deleted)).(*model.Emoji)
|
||||
|
||||
if returnedEmojis, err := Client.ListEmoji(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
found := false
|
||||
|
||||
for _, savedEmoji := range returnedEmojis {
|
||||
if deleted.Id == savedEmoji.Id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatalf("shouldn't have gotten deleted emoji %v", deleted.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateEmoji(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
EnableCustomEmoji := *th.App.Config().ServiceSettings.EnableCustomEmoji
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = false })
|
||||
|
||||
emoji := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
// try to create an emoji when they're disabled
|
||||
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
||||
t.Fatal("shouldn't be able to create an emoji when they're disabled")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
|
||||
// try to create a valid gif emoji when they're enabled
|
||||
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
emoji = emojiResult
|
||||
}
|
||||
|
||||
// try to create an emoji with a duplicate name
|
||||
emoji2 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: emoji.Name,
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji2, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
||||
t.Fatal("shouldn't be able to create an emoji with a duplicate name")
|
||||
}
|
||||
|
||||
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
||||
|
||||
// try to create a valid animated gif emoji
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestAnimatedGif(t, 10, 10, 10), "image.gif"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
emoji = emojiResult
|
||||
}
|
||||
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
||||
|
||||
// try to create a valid jpeg emoji
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestJpeg(t, 10, 10), "image.jpeg"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
emoji = emojiResult
|
||||
}
|
||||
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
||||
|
||||
// try to create a valid png emoji
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if emojiResult, err := Client.CreateEmoji(emoji, utils.CreateTestPng(t, 10, 10), "image.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
emoji = emojiResult
|
||||
}
|
||||
Client.MustGeneric(Client.DeleteEmoji(emoji.Id))
|
||||
|
||||
// try to create an emoji that's too wide
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 1000, 10), "image.gif"); err != nil {
|
||||
t.Fatal("should be able to create an emoji that's too wide by resizing it")
|
||||
}
|
||||
|
||||
// try to create an emoji that's too tall
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 1000), "image.gif"); err != nil {
|
||||
t.Fatal("should be able to create an emoji that's too tall by resizing it")
|
||||
}
|
||||
|
||||
// try to create an emoji that's too large
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji, utils.CreateTestAnimatedGif(t, 100, 100, 10000), "image.gif"); err == nil {
|
||||
t.Fatal("shouldn't be able to create an emoji that's too large")
|
||||
}
|
||||
|
||||
// try to create an emoji with data that isn't an image
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji, make([]byte, 100), "image.gif"); err == nil {
|
||||
t.Fatal("shouldn't be able to create an emoji with non-image data")
|
||||
}
|
||||
|
||||
// try to create an emoji as another user
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser2.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
if _, err := Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif"); err == nil {
|
||||
t.Fatal("shouldn't be able to create an emoji as another user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteEmoji(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
EnableCustomEmoji := *th.App.Config().ServiceSettings.EnableCustomEmoji
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = false })
|
||||
|
||||
emoji1 := createTestEmoji(t, th.App, &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}, utils.CreateTestGif(t, 10, 10))
|
||||
|
||||
if _, err := Client.DeleteEmoji(emoji1.Id); err == nil {
|
||||
t.Fatal("shouldn't have been able to delete an emoji when they're disabled")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
|
||||
if deleted, err := Client.DeleteEmoji(emoji1.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !deleted {
|
||||
t.Fatalf("should be able to delete your own emoji %v", emoji1.Id)
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteEmoji(emoji1.Id); err == nil {
|
||||
t.Fatal("shouldn't be able to delete an already-deleted emoji")
|
||||
}
|
||||
|
||||
emoji2 := createTestEmoji(t, th.App, &model.Emoji{
|
||||
CreatorId: th.BasicUser2.Id,
|
||||
Name: model.NewId(),
|
||||
}, utils.CreateTestGif(t, 10, 10))
|
||||
|
||||
if _, err := Client.DeleteEmoji(emoji2.Id); err == nil {
|
||||
t.Fatal("shouldn't be able to delete another user's emoji")
|
||||
}
|
||||
|
||||
if deleted, err := th.SystemAdminClient.DeleteEmoji(emoji2.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !deleted {
|
||||
t.Fatalf("system admin should be able to delete anyone's emoji %v", emoji2.Id)
|
||||
}
|
||||
}
|
||||
|
||||
func createTestEmoji(t *testing.T, a *app.App, emoji *model.Emoji, imageData []byte) *model.Emoji {
|
||||
emoji = store.Must(a.Srv.Store.Emoji().Save(emoji)).(*model.Emoji)
|
||||
|
||||
if _, err := a.WriteFile(bytes.NewReader(imageData), "emoji/"+emoji.Id+"/image"); err != nil {
|
||||
store.Must(a.Srv.Store.Emoji().Delete(emoji.Id, time.Now().Unix()))
|
||||
t.Fatalf("failed to write image: %v", err.Error())
|
||||
}
|
||||
|
||||
return emoji
|
||||
}
|
||||
|
||||
func TestGetEmojiImage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
EnableCustomEmoji := *th.App.Config().ServiceSettings.EnableCustomEmoji
|
||||
RestrictCustomEmojiCreation := *th.App.Config().ServiceSettings.RestrictCustomEmojiCreation
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = RestrictCustomEmojiCreation
|
||||
})
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ALL
|
||||
})
|
||||
|
||||
emoji1 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
emoji1 = Client.MustGeneric(Client.CreateEmoji(emoji1, utils.CreateTestGif(t, 10, 10), "image.gif")).(*model.Emoji)
|
||||
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji1.Id)) }()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = false })
|
||||
|
||||
if _, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji1.Id), "", ""); err == nil {
|
||||
t.Fatal("should've failed to get emoji image when disabled")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
|
||||
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji1.Id), "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if resp.Header.Get("Content-Type") != "image/gif" {
|
||||
t.Fatal("should've received a gif")
|
||||
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
||||
t.Fatalf("unable to identify received image: %v", err.Error())
|
||||
} else if imageType != "gif" {
|
||||
t.Fatal("should've received gif data")
|
||||
}
|
||||
|
||||
emoji2 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
emoji2 = Client.MustGeneric(Client.CreateEmoji(emoji2, utils.CreateTestAnimatedGif(t, 10, 10, 10), "image.gif")).(*model.Emoji)
|
||||
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji2.Id)) }()
|
||||
|
||||
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji2.Id), "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if resp.Header.Get("Content-Type") != "image/gif" {
|
||||
t.Fatal("should've received a gif")
|
||||
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
||||
t.Fatalf("unable to identify received image: %v", err.Error())
|
||||
} else if imageType != "gif" {
|
||||
t.Fatal("should've received gif data")
|
||||
}
|
||||
|
||||
emoji3 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
emoji3 = Client.MustGeneric(Client.CreateEmoji(emoji3, utils.CreateTestJpeg(t, 10, 10), "image.jpeg")).(*model.Emoji)
|
||||
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji3.Id)) }()
|
||||
|
||||
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji3.Id), "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if resp.Header.Get("Content-Type") != "image/jpeg" {
|
||||
t.Fatal("should've received a jpeg")
|
||||
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
||||
t.Fatalf("unable to identify received image: %v", err.Error())
|
||||
} else if imageType != "jpeg" {
|
||||
t.Fatal("should've received jpeg data")
|
||||
}
|
||||
|
||||
emoji4 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
emoji4 = Client.MustGeneric(Client.CreateEmoji(emoji4, utils.CreateTestPng(t, 10, 10), "image.png")).(*model.Emoji)
|
||||
defer func() { Client.MustGeneric(Client.DeleteEmoji(emoji4.Id)) }()
|
||||
|
||||
if resp, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji4.Id), "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if resp.Header.Get("Content-Type") != "image/png" {
|
||||
t.Fatal("should've received a png")
|
||||
} else if _, imageType, err := image.DecodeConfig(resp.Body); err != nil {
|
||||
t.Fatalf("unable to identify received image: %v", err.Error())
|
||||
} else if imageType != "png" {
|
||||
t.Fatal("should've received png data")
|
||||
}
|
||||
|
||||
emoji5 := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
emoji5 = Client.MustGeneric(Client.CreateEmoji(emoji5, utils.CreateTestPng(t, 10, 10), "image.png")).(*model.Emoji)
|
||||
Client.MustGeneric(Client.DeleteEmoji(emoji5.Id))
|
||||
|
||||
if _, err := Client.DoApiGet(Client.GetCustomEmojiImageUrl(emoji5.Id), "", ""); err == nil {
|
||||
t.Fatal("should've failed to get image for deleted emoji")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResizeEmoji(t *testing.T) {
|
||||
// try to resize a jpeg image within MaxEmojiWidth and MaxEmojiHeight
|
||||
small_img_data := utils.CreateTestJpeg(t, app.MaxEmojiWidth, app.MaxEmojiHeight)
|
||||
if small_img, _, err := image.Decode(bytes.NewReader(small_img_data)); err != nil {
|
||||
t.Fatal("failed to decode jpeg bytes to image.Image")
|
||||
} else {
|
||||
resized_img := resizeEmoji(small_img, small_img.Bounds().Dx(), small_img.Bounds().Dy())
|
||||
if resized_img.Bounds().Dx() > app.MaxEmojiWidth || resized_img.Bounds().Dy() > app.MaxEmojiHeight {
|
||||
t.Fatal("resized jpeg width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
||||
}
|
||||
if resized_img != small_img {
|
||||
t.Fatal("should've returned small_img itself")
|
||||
}
|
||||
}
|
||||
// try to resize a jpeg image
|
||||
jpeg_data := utils.CreateTestJpeg(t, 256, 256)
|
||||
if jpeg_img, _, err := image.Decode(bytes.NewReader(jpeg_data)); err != nil {
|
||||
t.Fatal("failed to decode jpeg bytes to image.Image")
|
||||
} else {
|
||||
resized_jpeg := resizeEmoji(jpeg_img, jpeg_img.Bounds().Dx(), jpeg_img.Bounds().Dy())
|
||||
if resized_jpeg.Bounds().Dx() > app.MaxEmojiWidth || resized_jpeg.Bounds().Dy() > app.MaxEmojiHeight {
|
||||
t.Fatal("resized jpeg width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
||||
}
|
||||
}
|
||||
// try to resize a png image
|
||||
png_data := utils.CreateTestJpeg(t, 256, 256)
|
||||
if png_img, _, err := image.Decode(bytes.NewReader(png_data)); err != nil {
|
||||
t.Fatal("failed to decode png bytes to image.Image")
|
||||
} else {
|
||||
resized_png := resizeEmoji(png_img, png_img.Bounds().Dx(), png_img.Bounds().Dy())
|
||||
if resized_png.Bounds().Dx() > app.MaxEmojiWidth || resized_png.Bounds().Dy() > app.MaxEmojiHeight {
|
||||
t.Fatal("resized png width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
||||
}
|
||||
}
|
||||
// try to resize an animated gif
|
||||
gif_data := utils.CreateTestAnimatedGif(t, 256, 256, 10)
|
||||
if gif_img, err := gif.DecodeAll(bytes.NewReader(gif_data)); err != nil {
|
||||
t.Fatal("failed to decode gif bytes to gif.GIF")
|
||||
} else {
|
||||
resized_gif := resizeEmojiGif(gif_img)
|
||||
if resized_gif.Config.Width > app.MaxEmojiWidth || resized_gif.Config.Height > app.MaxEmojiHeight {
|
||||
t.Fatal("resized gif width and height should not be greater than MaxEmojiWidth or MaxEmojiHeight")
|
||||
}
|
||||
if len(resized_gif.Image) != len(gif_img.Image) {
|
||||
t.Fatal("resized gif should have the same number of frames as original gif")
|
||||
}
|
||||
}
|
||||
}
|
||||
333
api/file.go
333
api/file.go
@@ -1,333 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
PREVIEW_IMAGE_TYPE = "image/jpeg"
|
||||
THUMBNAIL_IMAGE_TYPE = "image/jpeg"
|
||||
)
|
||||
|
||||
var UNSAFE_CONTENT_TYPES = [...]string{
|
||||
"application/javascript",
|
||||
"application/ecmascript",
|
||||
"text/javascript",
|
||||
"text/ecmascript",
|
||||
"application/x-javascript",
|
||||
"text/html",
|
||||
}
|
||||
|
||||
func (api *API) InitFile() {
|
||||
api.BaseRoutes.TeamFiles.Handle("/upload", api.ApiUserRequired(uploadFile)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.NeedFile.Handle("/get", api.ApiUserRequiredTrustRequester(getFile)).Methods("GET")
|
||||
api.BaseRoutes.NeedFile.Handle("/get_thumbnail", api.ApiUserRequiredTrustRequester(getFileThumbnail)).Methods("GET")
|
||||
api.BaseRoutes.NeedFile.Handle("/get_preview", api.ApiUserRequiredTrustRequester(getFilePreview)).Methods("GET")
|
||||
api.BaseRoutes.NeedFile.Handle("/get_info", api.ApiUserRequired(getFileInfo)).Methods("GET")
|
||||
api.BaseRoutes.NeedFile.Handle("/get_public_link", api.ApiUserRequired(getPublicLink)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Public.Handle("/files/{file_id:[A-Za-z0-9]+}/get", api.ApiAppHandlerTrustRequesterIndependent(getPublicFile)).Methods("GET")
|
||||
api.BaseRoutes.Public.Handle("/files/get/{team_id:[A-Za-z0-9]+}/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:(?:[A-Za-z0-9]+/)?.+(?:\\.[A-Za-z0-9]{3,})?}", api.ApiAppHandlerTrustRequesterIndependent(getPublicFileOld)).Methods("GET")
|
||||
}
|
||||
|
||||
func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().FileSettings.EnableFileAttachments {
|
||||
c.Err = model.NewAppError("uploadFile", "api.file.attachments.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize {
|
||||
c.Err = model.NewAppError("uploadFile", "api.file.upload_file.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
|
||||
props := m.Value
|
||||
if len(props["channel_id"]) == 0 {
|
||||
c.SetInvalidParam("uploadFile", "channel_id")
|
||||
return
|
||||
}
|
||||
channelId := props["channel_id"][0]
|
||||
if len(channelId) == 0 {
|
||||
c.SetInvalidParam("uploadFile", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_UPLOAD_FILE) {
|
||||
c.SetPermissionError(model.PERMISSION_UPLOAD_FILE)
|
||||
return
|
||||
}
|
||||
|
||||
resStruct, err := c.App.UploadMultipartFiles(c.TeamId, channelId, c.Session.UserId, m.File["files"], m.Value["client_ids"])
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(resStruct.ToJson()))
|
||||
}
|
||||
|
||||
func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
info, err := getFileInfoForRequest(c, r, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if data, err := c.App.ReadFile(info.Path); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
} else if err := writeFileResponse(info.Name, info.MimeType, data, w, r); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
info, err := getFileInfoForRequest(c, r, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if info.ThumbnailPath == "" {
|
||||
c.Err = model.NewAppError("getFileThumbnail", "api.file.get_file_thumbnail.no_thumbnail.app_error", nil, "file_id="+info.Id, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if data, err := c.App.ReadFile(info.ThumbnailPath); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
} else if err := writeFileResponse(info.Name, THUMBNAIL_IMAGE_TYPE, data, w, r); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
info, err := getFileInfoForRequest(c, r, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if info.PreviewPath == "" {
|
||||
c.Err = model.NewAppError("getFilePreview", "api.file.get_file_preview.no_preview.app_error", nil, "file_id="+info.Id, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if data, err := c.App.ReadFile(info.PreviewPath); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
} else if err := writeFileResponse(info.Name, PREVIEW_IMAGE_TYPE, data, w, r); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
info, err := getFileInfoForRequest(c, r, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
|
||||
w.Write([]byte(info.ToJson()))
|
||||
}
|
||||
|
||||
func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.Config().FileSettings.EnablePublicLink {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
info, err := getFileInfoForRequest(c, r, false)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
hash := r.URL.Query().Get("h")
|
||||
|
||||
if len(hash) > 0 {
|
||||
correctHash := app.GeneratePublicLinkHash(info.Id, *c.App.Config().FileSettings.PublicLinkSalt)
|
||||
|
||||
if hash != correctHash {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey())
|
||||
return
|
||||
}
|
||||
|
||||
if data, err := c.App.ReadFile(info.Path); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
} else if err := writeFileResponse(info.Name, info.MimeType, data, w, r); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getFileInfoForRequest(c *Context, r *http.Request, requireFileVisible bool) (*model.FileInfo, *model.AppError) {
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
return nil, model.NewAppError("getFileInfoForRequest", "api.file.get_info_for_request.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
fileId := params["file_id"]
|
||||
if len(fileId) != 26 {
|
||||
return nil, NewInvalidParamError("getFileInfoForRequest", "file_id")
|
||||
}
|
||||
|
||||
info, err := c.App.GetFileInfo(fileId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only let users access files visible in a channel, unless they're the one who uploaded the file
|
||||
if info.CreatorId != c.Session.UserId {
|
||||
if len(info.PostId) == 0 {
|
||||
err := model.NewAppError("getFileInfoForRequest", "api.file.get_file_info_for_request.no_post.app_error", nil, "file_id="+fileId, http.StatusBadRequest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if requireFileVisible {
|
||||
if !c.App.SessionHasPermissionToChannelByPost(c.Session, info.PostId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return nil, c.Err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func getPublicFileOld(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_public_file_old.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
} else if !c.App.Config().FileSettings.EnablePublicLink {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
teamId := params["team_id"]
|
||||
channelId := params["channel_id"]
|
||||
userId := params["user_id"]
|
||||
filename := params["filename"]
|
||||
|
||||
hash := r.URL.Query().Get("h")
|
||||
|
||||
if len(hash) > 0 {
|
||||
correctHash := app.GeneratePublicLinkHash(filename, *c.App.Config().FileSettings.PublicLinkSalt)
|
||||
|
||||
if hash != correctHash {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
path := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
|
||||
|
||||
var info *model.FileInfo
|
||||
if result := <-c.App.Srv.Store.FileInfo().GetByPath(path); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
info = result.Data.(*model.FileInfo)
|
||||
}
|
||||
|
||||
if len(info.PostId) == 0 {
|
||||
c.Err = model.NewAppError("getPublicFileOld", "api.file.get_public_file_old.no_post.app_error", nil, "file_id="+info.Id, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if data, err := c.App.ReadFile(info.Path); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusNotFound
|
||||
} else if err := writeFileResponse(info.Name, info.MimeType, data, w, r); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func writeFileResponse(filename string, contentType string, bytes []byte, w http.ResponseWriter, r *http.Request) *model.AppError {
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, private")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(bytes)))
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
} else {
|
||||
for _, unsafeContentType := range UNSAFE_CONTENT_TYPES {
|
||||
if strings.HasPrefix(contentType, unsafeContentType) {
|
||||
contentType = "text/plain"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+url.QueryEscape(filename))
|
||||
|
||||
// prevent file links from being embedded in iframes
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'")
|
||||
|
||||
w.Write(bytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.Config().FileSettings.EnablePublicLink {
|
||||
c.Err = model.NewAppError("getPublicLink", "api.file.get_public_link.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
info, err := getFileInfoForRequest(c, r, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(info.PostId) == 0 {
|
||||
c.Err = model.NewAppError("getPublicLink", "api.file.get_public_link.no_post.app_error", nil, "file_id="+info.Id, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.StringToJson(c.App.GeneratePublicLinkV3(c.GetSiteURLHeader(), info))))
|
||||
}
|
||||
950
api/file_test.go
950
api/file_test.go
@@ -1,950 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
|
||||
s3 "github.com/minio/minio-go"
|
||||
"github.com/minio/minio-go/pkg/credentials"
|
||||
)
|
||||
|
||||
func TestUploadFile(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Logf("skipping because no file driver is enabled")
|
||||
return
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
team := th.BasicTeam
|
||||
user := th.BasicUser
|
||||
channel := th.BasicChannel
|
||||
|
||||
var uploadInfo *model.FileInfo
|
||||
var data []byte
|
||||
var err error
|
||||
if data, err = readTestFile("test.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if resp, err := Client.UploadPostAttachment(data, channel.Id, "test.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(resp.FileInfos) != 1 {
|
||||
t.Fatal("should've returned a single file infos")
|
||||
} else {
|
||||
uploadInfo = resp.FileInfos[0]
|
||||
}
|
||||
|
||||
// The returned file info from the upload call will be missing some fields that will be stored in the database
|
||||
if uploadInfo.CreatorId != user.Id {
|
||||
t.Fatal("file should be assigned to user")
|
||||
} else if uploadInfo.PostId != "" {
|
||||
t.Fatal("file shouldn't have a post")
|
||||
} else if uploadInfo.Path != "" {
|
||||
t.Fatal("file path should not be set on returned info")
|
||||
} else if uploadInfo.ThumbnailPath != "" {
|
||||
t.Fatal("file thumbnail path should not be set on returned info")
|
||||
} else if uploadInfo.PreviewPath != "" {
|
||||
t.Fatal("file preview path should not be set on returned info")
|
||||
}
|
||||
|
||||
var info *model.FileInfo
|
||||
if result := <-th.App.Srv.Store.FileInfo().Get(uploadInfo.Id); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
info = result.Data.(*model.FileInfo)
|
||||
}
|
||||
|
||||
if info.Id != uploadInfo.Id {
|
||||
t.Fatal("file id from response should match one stored in database")
|
||||
} else if info.CreatorId != user.Id {
|
||||
t.Fatal("file should be assigned to user")
|
||||
} else if info.PostId != "" {
|
||||
t.Fatal("file shouldn't have a post")
|
||||
} else if info.Path == "" {
|
||||
t.Fatal("file path should be set in database")
|
||||
} else if info.ThumbnailPath == "" {
|
||||
t.Fatal("file thumbnail path should be set in database")
|
||||
} else if info.PreviewPath == "" {
|
||||
t.Fatal("file preview path should be set in database")
|
||||
}
|
||||
|
||||
date := time.Now().Format("20060102")
|
||||
|
||||
// This also makes sure that the relative path provided above is sanitized out
|
||||
expectedPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test.png", date, team.Id, channel.Id, user.Id, info.Id)
|
||||
if info.Path != expectedPath {
|
||||
t.Logf("file is saved in %v", info.Path)
|
||||
t.Fatalf("file should've been saved in %v", expectedPath)
|
||||
}
|
||||
|
||||
expectedThumbnailPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_thumb.jpg", date, team.Id, channel.Id, user.Id, info.Id)
|
||||
if info.ThumbnailPath != expectedThumbnailPath {
|
||||
t.Logf("file thumbnail is saved in %v", info.ThumbnailPath)
|
||||
t.Fatalf("file thumbnail should've been saved in %v", expectedThumbnailPath)
|
||||
}
|
||||
|
||||
expectedPreviewPath := fmt.Sprintf("%v/teams/%v/channels/%v/users/%v/%v/test_preview.jpg", date, team.Id, channel.Id, user.Id, info.Id)
|
||||
if info.PreviewPath != expectedPreviewPath {
|
||||
t.Logf("file preview is saved in %v", info.PreviewPath)
|
||||
t.Fatalf("file preview should've been saved in %v", expectedPreviewPath)
|
||||
}
|
||||
|
||||
if _, err := Client.UploadPostAttachment(data, model.NewId(), "test.png"); err == nil || err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
if _, err := Client.UploadPostAttachment(data, "../../junk", "test.png"); err == nil || err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.UploadPostAttachment(data, model.NewId(), "test.png"); err == nil || err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
if _, err := th.SystemAdminClient.UploadPostAttachment(data, "../../junk", "test.png"); err == nil || err.StatusCode != http.StatusForbidden {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
enableFileAttachments := *th.App.Config().FileSettings.EnableFileAttachments
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = enableFileAttachments })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.EnableFileAttachments = false })
|
||||
|
||||
if data, err := readTestFile("test.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err = Client.UploadPostAttachment(data, channel.Id, "test.png"); err == nil {
|
||||
t.Fatal("should have errored")
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if err := cleanupTestFile(info, &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFileInfo(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
user := th.BasicUser
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
if data, err := readTestFile("test.png"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
info, err := Client.GetFileInfo(fileId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if info.Id != fileId {
|
||||
t.Fatal("got incorrect file")
|
||||
} else if info.CreatorId != user.Id {
|
||||
t.Fatal("file should be assigned to user")
|
||||
} else if info.PostId != "" {
|
||||
t.Fatal("file shouldn't have a post")
|
||||
} else if info.Path != "" {
|
||||
t.Fatal("file path shouldn't have been returned to client")
|
||||
} else if info.ThumbnailPath != "" {
|
||||
t.Fatal("file thumbnail path shouldn't have been returned to client")
|
||||
} else if info.PreviewPath != "" {
|
||||
t.Fatal("file preview path shouldn't have been returned to client")
|
||||
} else if info.MimeType != "image/png" {
|
||||
t.Fatal("mime type should've been image/png")
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Other user shouldn't be able to get file info for this file before it's attached to a post
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.GetFileInfo(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file info before it's attached to a post")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
// Other user shouldn't be able to get file info for this file if they're not in the channel for it
|
||||
if _, err := Client.GetFileInfo(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file info when not in channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
// Other user should now be able to get file info
|
||||
if info2, err := Client.GetFileInfo(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if info2.Id != fileId {
|
||||
t.Fatal("other user got incorrect file")
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFile(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if body, err := Client.GetFile(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
received, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(received) != len(data) {
|
||||
t.Fatal("received file should be the same size as the sent one")
|
||||
}
|
||||
|
||||
for i := range data {
|
||||
if data[i] != received[i] {
|
||||
t.Fatal("received file didn't match sent one")
|
||||
}
|
||||
}
|
||||
|
||||
body.Close()
|
||||
}
|
||||
|
||||
// Other user shouldn't be able to get file for this file before it's attached to a post
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.GetFile(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
// Other user shouldn't be able to get file for this file if they're not in the channel for it
|
||||
if _, err := Client.GetFile(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file when not in channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
// Other user should now be able to get file
|
||||
if body, err := Client.GetFile(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
received, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(received) != len(data) {
|
||||
t.Fatal("received file should be the same size as the sent one")
|
||||
}
|
||||
|
||||
for i := range data {
|
||||
if data[i] != received[i] {
|
||||
t.Fatal("received file didn't match sent one")
|
||||
}
|
||||
}
|
||||
|
||||
body.Close()
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFileThumbnail(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if body, err := Client.GetFileThumbnail(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
// Other user shouldn't be able to get thumbnail for this file before it's attached to a post
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.GetFileThumbnail(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
// Other user shouldn't be able to get thumbnail for this file if they're not in the channel for it
|
||||
if _, err := Client.GetFileThumbnail(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file when not in channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
// Other user should now be able to get thumbnail
|
||||
if body, err := Client.GetFileThumbnail(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFilePreview(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if body, err := Client.GetFilePreview(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
// Other user shouldn't be able to get preview for this file before it's attached to a post
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.GetFilePreview(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file before it's attached to a post")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
// Other user shouldn't be able to get preview for this file if they're not in the channel for it
|
||||
if _, err := Client.GetFilePreview(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file when not in channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
// Other user should now be able to get preview
|
||||
if body, err := Client.GetFilePreview(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicFile(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
enablePublicLink := th.App.Config().FileSettings.EnablePublicLink
|
||||
publicLinkSalt := *th.App.Config().FileSettings.PublicLinkSalt
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = publicLinkSalt })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() })
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
link := Client.MustGeneric(Client.GetPublicLink(fileId)).(string)
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK {
|
||||
t.Log(link)
|
||||
t.Fatal("failed to get image with public link", err)
|
||||
}
|
||||
|
||||
if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link without hash", resp.Status)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false })
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("should've failed to get image with disabled public link")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
|
||||
// test after the salt has changed
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() })
|
||||
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link after salt changed")
|
||||
}
|
||||
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link after salt changed")
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicFileOld(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
enablePublicLink := th.App.Config().FileSettings.EnablePublicLink
|
||||
publicLinkSalt := *th.App.Config().FileSettings.PublicLinkSalt
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = publicLinkSalt })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() })
|
||||
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
//fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
fileId = model.NewId()
|
||||
fileInfo := model.FileInfo{
|
||||
Id: fileId,
|
||||
CreateAt: model.GetMillis(),
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Path: fmt.Sprintf("teams/%s/channels/%s/users/%s/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId, "test.png"),
|
||||
}
|
||||
store.Must(th.App.Srv.Store.FileInfo().Save(&fileInfo))
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId), "test.png")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
// reconstruct old style of link
|
||||
siteURL := fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port)
|
||||
link := generatePublicLinkOld(siteURL, th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId+"/test.png", *th.App.Config().FileSettings.PublicLinkSalt)
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if resp, err := http.Get(link); err != nil || resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("failed to get image with public link err=%v resp=%v", err, resp)
|
||||
}
|
||||
|
||||
if resp, err := http.Get(link[:strings.LastIndex(link, "?")]); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link without hash", resp.Status)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false })
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusNotImplemented {
|
||||
t.Fatal("should've failed to get image with disabled public link")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
|
||||
// test after the salt has changed
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.PublicLinkSalt = model.NewId() })
|
||||
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link after salt changed")
|
||||
}
|
||||
|
||||
if resp, err := http.Get(link); err == nil && resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatal("should've failed to get image with public link after salt changed")
|
||||
}
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generatePublicLinkOld(siteURL, teamId, channelId, userId, filename, salt string) string {
|
||||
hash := app.GeneratePublicLinkHash(filename, salt)
|
||||
return fmt.Sprintf("%s%s/public/files/get/%s/%s/%s/%s?h=%s", siteURL, model.API_URL_SUFFIX_V3, teamId, channelId, userId, filename, hash)
|
||||
}
|
||||
|
||||
func TestGetPublicLink(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
enablePublicLink := th.App.Config().FileSettings.EnablePublicLink
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = enablePublicLink })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
|
||||
var fileId string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId = Client.MustGeneric(Client.UploadPostAttachment(data, channel.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
}
|
||||
|
||||
if _, err := Client.GetPublicLink(fileId); err == nil {
|
||||
t.Fatal("should've failed to get public link before file is attached to a post")
|
||||
}
|
||||
|
||||
// Hacky way to assign file to a post (usually would be done by CreatePost call)
|
||||
store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id))
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = false })
|
||||
|
||||
if _, err := Client.GetPublicLink(fileId); err == nil {
|
||||
t.Fatal("should've failed to get public link when disabled")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings.EnablePublicLink = true })
|
||||
|
||||
if link, err := Client.GetPublicLink(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if link == "" {
|
||||
t.Fatal("should've received public link")
|
||||
}
|
||||
|
||||
// Other user shouldn't be able to get public link for this file if they're not in the channel for it
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.GetPublicLink(fileId); err == nil {
|
||||
t.Fatal("other user shouldn't be able to get file when not in channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
// Other user should now be able to get public link
|
||||
if link, err := Client.GetPublicLink(fileId); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if link == "" {
|
||||
t.Fatal("should've received public link")
|
||||
}
|
||||
|
||||
// Wait a bit for files to ready
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if err := cleanupTestFile(store.Must(th.App.Srv.Store.FileInfo().Get(fileId)).(*model.FileInfo), &th.App.Config().FileSettings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateFilenamesToFileInfos(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user1 := th.BasicUser
|
||||
|
||||
channel1 := Client.Must(Client.CreateChannel(&model.Channel{
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
// No TeamId set to simulate a direct channel
|
||||
})).Data.(*model.Channel)
|
||||
|
||||
var fileId1 string
|
||||
var fileId2 string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
||||
fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", th.BasicTeam.Id, channel1.Id, user1.Id, fileId2), "test.png")
|
||||
}
|
||||
|
||||
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
||||
post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel1.Id,
|
||||
Message: "test",
|
||||
Filenames: []string{
|
||||
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png"),
|
||||
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"),
|
||||
fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId2, "test.png"), // duplicate a filename to recreate a rare bug
|
||||
},
|
||||
})).(*model.Post)
|
||||
|
||||
if post1.FileIds != nil && len(post1.FileIds) > 0 {
|
||||
t.Fatal("post shouldn't have file ids")
|
||||
} else if post1.Filenames == nil || len(post1.Filenames) != 3 {
|
||||
t.Fatal("post should have filenames")
|
||||
}
|
||||
|
||||
// Indirectly call migrateFilenamesToFileInfos by calling Client.GetFileInfosForPost
|
||||
var infos []*model.FileInfo
|
||||
if infosResult, err := Client.GetFileInfosForPost(post1.ChannelId, post1.Id, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
infos = infosResult
|
||||
}
|
||||
|
||||
if len(infos) != 2 {
|
||||
t.Log(infos)
|
||||
t.Fatal("should've had 2 infos after migration")
|
||||
} else if infos[0].Path != "" || infos[0].ThumbnailPath != "" || infos[0].PreviewPath != "" {
|
||||
t.Fatal("shouldn't return paths to client")
|
||||
}
|
||||
|
||||
// Should be able to get files after migration
|
||||
if body, err := Client.GetFile(infos[0].Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
if body, err := Client.GetFile(infos[1].Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
// Make sure we aren't generating a new set of FileInfos on a second call to GetFileInfosForPost
|
||||
if infos2 := Client.MustGeneric(Client.GetFileInfosForPost(post1.ChannelId, post1.Id, "")).([]*model.FileInfo); len(infos2) != len(infos) {
|
||||
t.Fatal("should've received the same 2 infos after second call")
|
||||
} else if (infos[0].Id != infos2[0].Id && infos[0].Id != infos2[1].Id) || (infos[1].Id != infos2[0].Id && infos[1].Id != infos2[1].Id) {
|
||||
t.Fatal("should've returned the exact same 2 infos after second call")
|
||||
}
|
||||
|
||||
if result, err := Client.GetPost(post1.ChannelId, post1.Id, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if post := result.Data.(*model.PostList).Posts[post1.Id]; len(post.Filenames) != 0 {
|
||||
t.Fatal("post shouldn't have filenames")
|
||||
} else if len(post.FileIds) != 2 {
|
||||
t.Fatal("post should have 2 file ids")
|
||||
} else if (infos[0].Id != post.FileIds[0] && infos[0].Id != post.FileIds[1]) || (infos[1].Id != post.FileIds[0] && infos[1].Id != post.FileIds[1]) {
|
||||
t.Fatal("post file ids should match GetFileInfosForPost results")
|
||||
}
|
||||
}
|
||||
|
||||
func uploadFileOld(t *testing.T, data []byte, dest string, filename string) {
|
||||
os.MkdirAll(dest, os.ModePerm)
|
||||
eFile, err := os.Create(dest + "/" + filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer eFile.Close()
|
||||
|
||||
_, err = io.Copy(eFile, bytes.NewReader(data)) // first var shows number of bytes
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = eFile.Sync()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindTeamIdForFilename(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user1 := th.BasicUser
|
||||
|
||||
team1 := th.BasicTeam
|
||||
team2 := th.CreateTeam(th.BasicClient)
|
||||
|
||||
channel1 := th.BasicChannel
|
||||
|
||||
Client.SetTeamId(team2.Id)
|
||||
channel2 := Client.Must(Client.CreateChannel(&model.Channel{
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
// No TeamId set to simulate a direct channel
|
||||
})).Data.(*model.Channel)
|
||||
Client.SetTeamId(team1.Id)
|
||||
|
||||
var fileId1 string
|
||||
var fileId2 string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
||||
|
||||
Client.SetTeamId(team2.Id)
|
||||
fileId2 = Client.MustGeneric(Client.UploadPostAttachment(data, channel2.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team2.Id, channel2.Id, user1.Id, fileId2), "test.png")
|
||||
Client.SetTeamId(team1.Id)
|
||||
}
|
||||
|
||||
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
||||
post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel1.Id,
|
||||
Message: "test",
|
||||
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")},
|
||||
})).(*model.Post)
|
||||
|
||||
if teamId := th.App.FindTeamIdForFilename(post1, post1.Filenames[0]); teamId != team1.Id {
|
||||
t.Log(teamId)
|
||||
t.Fatal("file should've been found under team1")
|
||||
}
|
||||
|
||||
Client.SetTeamId(team2.Id)
|
||||
post2 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel2.Id,
|
||||
Message: "test",
|
||||
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel2.Id, user1.Id, fileId2, "test.png")},
|
||||
})).(*model.Post)
|
||||
Client.SetTeamId(team1.Id)
|
||||
|
||||
if teamId := th.App.FindTeamIdForFilename(post2, post2.Filenames[0]); teamId != team2.Id {
|
||||
t.Fatal("file should've been found under team2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfoForFilename(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if *th.App.Config().FileSettings.DriverName == "" {
|
||||
t.Skip("skipping because no file driver is enabled")
|
||||
}
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user1 := th.BasicUser
|
||||
|
||||
team1 := th.BasicTeam
|
||||
|
||||
channel1 := th.BasicChannel
|
||||
|
||||
var fileId1 string
|
||||
var path string
|
||||
var thumbnailPath string
|
||||
var previewPath string
|
||||
data, err := readTestFile("test.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
fileId1 = Client.MustGeneric(Client.UploadPostAttachment(data, channel1.Id, "test.png")).(*model.FileUploadResponse).FileInfos[0].Id
|
||||
uploadFileOld(t, data, fmt.Sprintf("data/teams/%s/channels/%s/users/%s/%s", team1.Id, channel1.Id, user1.Id, fileId1), "test.png")
|
||||
path = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).Path
|
||||
thumbnailPath = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).ThumbnailPath
|
||||
previewPath = store.Must(th.App.Srv.Store.FileInfo().Get(fileId1)).(*model.FileInfo).PreviewPath
|
||||
}
|
||||
|
||||
// Bypass the Client whenever possible since we're trying to simulate a pre-3.5 post
|
||||
post1 := store.Must(th.App.Srv.Store.Post().Save(&model.Post{
|
||||
UserId: user1.Id,
|
||||
ChannelId: channel1.Id,
|
||||
Message: "test",
|
||||
Filenames: []string{fmt.Sprintf("/%s/%s/%s/%s", channel1.Id, user1.Id, fileId1, "test.png")},
|
||||
})).(*model.Post)
|
||||
|
||||
date := time.Now().Format("20060102")
|
||||
|
||||
if info := th.App.GetInfoForFilename(post1, team1.Id, post1.Filenames[0]); info == nil {
|
||||
t.Fatal("info shouldn't be nil")
|
||||
} else if info.Id == "" {
|
||||
t.Fatal("info.Id shouldn't be empty")
|
||||
} else if info.CreatorId != user1.Id {
|
||||
t.Fatal("incorrect user id")
|
||||
} else if info.PostId != post1.Id {
|
||||
t.Fatal("incorrect user id")
|
||||
} else if fmt.Sprintf("%s/%s", date, info.Path) != path {
|
||||
t.Fatal("incorrect path")
|
||||
} else if fmt.Sprintf("%s/%s", date, info.ThumbnailPath) != thumbnailPath {
|
||||
t.Fatal("incorrect thumbnail path")
|
||||
} else if fmt.Sprintf("%s/%s", date, info.PreviewPath) != previewPath {
|
||||
t.Fatal("incorrect preview path")
|
||||
} else if info.Name != "test.png" {
|
||||
t.Fatal("incorrect name")
|
||||
}
|
||||
}
|
||||
|
||||
func readTestFile(name string) ([]byte, error) {
|
||||
path, _ := utils.FindDir("tests")
|
||||
file, err := os.Open(filepath.Join(path, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data := &bytes.Buffer{}
|
||||
if _, err := io.Copy(data, file); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return data.Bytes(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
|
||||
// If signV2 input is false, function always returns signature v4.
|
||||
//
|
||||
// Additionally this function also takes a user defined region, if set
|
||||
// disables automatic region lookup.
|
||||
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool, region string) (*s3.Client, error) {
|
||||
var creds *credentials.Credentials
|
||||
if signV2 {
|
||||
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV2)
|
||||
} else {
|
||||
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV4)
|
||||
}
|
||||
return s3.NewWithCredentials(endpoint, creds, secure, region)
|
||||
}
|
||||
|
||||
func cleanupTestFile(info *model.FileInfo, settings *model.FileSettings) error {
|
||||
if *settings.DriverName == model.IMAGE_DRIVER_S3 {
|
||||
endpoint := settings.AmazonS3Endpoint
|
||||
accessKey := settings.AmazonS3AccessKeyId
|
||||
secretKey := settings.AmazonS3SecretAccessKey
|
||||
secure := *settings.AmazonS3SSL
|
||||
signV2 := *settings.AmazonS3SignV2
|
||||
region := settings.AmazonS3Region
|
||||
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := settings.AmazonS3Bucket
|
||||
if err := s3Clnt.RemoveObject(bucket, info.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.ThumbnailPath != "" {
|
||||
if err := s3Clnt.RemoveObject(bucket, info.ThumbnailPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if info.PreviewPath != "" {
|
||||
if err := s3Clnt.RemoveObject(bucket, info.PreviewPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if *settings.DriverName == model.IMAGE_DRIVER_LOCAL {
|
||||
if err := os.Remove(settings.Directory + info.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.ThumbnailPath != "" {
|
||||
if err := os.Remove(settings.Directory + info.ThumbnailPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if info.PreviewPath != "" {
|
||||
if err := os.Remove(settings.Directory + info.PreviewPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitGeneral() {
|
||||
api.BaseRoutes.General.Handle("/client_props", api.ApiAppHandler(getClientConfig)).Methods("GET")
|
||||
api.BaseRoutes.General.Handle("/log_client", api.ApiAppHandler(logClient)).Methods("POST")
|
||||
api.BaseRoutes.General.Handle("/ping", api.ApiAppHandler(ping)).Methods("GET")
|
||||
}
|
||||
|
||||
func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(model.MapToJson(c.App.ClientConfig())))
|
||||
}
|
||||
|
||||
func logClient(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
forceToDebug := false
|
||||
|
||||
if !*c.App.Config().ServiceSettings.EnableDeveloper {
|
||||
if c.Session.UserId == "" {
|
||||
c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
forceToDebug = true
|
||||
}
|
||||
}
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
lvl := m["level"]
|
||||
msg := m["message"]
|
||||
|
||||
// filter out javascript errors from franz that are polluting the log files
|
||||
if strings.Contains(msg, "/franz") {
|
||||
forceToDebug = true
|
||||
}
|
||||
|
||||
if len(msg) > 400 {
|
||||
msg = msg[0:399]
|
||||
}
|
||||
|
||||
if lvl == "ERROR" {
|
||||
err := &model.AppError{}
|
||||
err.Message = msg
|
||||
err.Id = msg
|
||||
err.Where = "client"
|
||||
|
||||
if forceToDebug {
|
||||
c.LogDebug(err)
|
||||
} else {
|
||||
c.LogError(err)
|
||||
}
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func ping(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
m := make(map[string]string)
|
||||
m["version"] = model.CurrentVersion
|
||||
m["server_time"] = fmt.Sprintf("%v", model.GetMillis())
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestGetClientProperties(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if props, err := th.BasicClient.GetClientProperties(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if len(props["Version"]) == 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogClient(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if ret, _ := th.BasicClient.LogClient("this is a test"); !ret {
|
||||
t.Fatal("failed to log")
|
||||
}
|
||||
|
||||
enableDeveloper := *th.App.Config().ServiceSettings.EnableDeveloper
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = enableDeveloper })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = false })
|
||||
|
||||
th.BasicClient.Logout()
|
||||
|
||||
if _, err := th.BasicClient.LogClient("this is a test"); err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
|
||||
|
||||
if ret, _ := th.BasicClient.LogClient("this is a test"); !ret {
|
||||
t.Fatal("failed to log")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPing(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
if m, err := th.BasicClient.GetPing(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if len(m["version"]) == 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
100
api/license.go
100
api/license.go
@@ -1,100 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitLicense() {
|
||||
api.BaseRoutes.License.Handle("/add", api.ApiAdminSystemRequired(addLicense)).Methods("POST")
|
||||
api.BaseRoutes.License.Handle("/remove", api.ApiAdminSystemRequired(removeLicense)).Methods("POST")
|
||||
api.BaseRoutes.License.Handle("/client_config", api.ApiAppHandler(getClientLicenceConfig)).Methods("GET")
|
||||
}
|
||||
|
||||
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.LogAudit("attempt")
|
||||
err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
|
||||
fileArray, ok := m.File["license"]
|
||||
if !ok {
|
||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(fileArray) <= 0 {
|
||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.array.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fileData := fileArray[0]
|
||||
|
||||
file, err := fileData.Open()
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
io.Copy(buf, file)
|
||||
|
||||
if license, err := c.App.SaveLicense(buf.Bytes()); err != nil {
|
||||
if err.Id == model.EXPIRED_LICENSE_ERROR {
|
||||
c.LogAudit("failed - expired or non-started license")
|
||||
} else if err.Id == model.INVALID_LICENSE_ERROR {
|
||||
c.LogAudit("failed - invalid license")
|
||||
} else {
|
||||
c.LogAudit("failed - unable to save license")
|
||||
}
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(license.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.LogAudit("")
|
||||
|
||||
if err := c.App.RemoveLicense(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
useSanitizedLicense := !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM)
|
||||
|
||||
etag := c.App.GetClientLicenseEtag(useSanitizedLicense)
|
||||
if c.HandleEtag(etag, "Get Client License Config", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
var clientLicense map[string]string
|
||||
|
||||
if useSanitizedLicense {
|
||||
clientLicense = c.App.ClientLicense()
|
||||
} else {
|
||||
clientLicense = c.App.GetSanitizedClientLicense()
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.Write([]byte(model.MapToJson(clientLicense)))
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetLicenceConfig(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
if result, err := Client.GetClientLicenceConfig(""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
cfg := result.Data.(map[string]string)
|
||||
|
||||
if _, ok := cfg["IsLicensed"]; !ok {
|
||||
t.Fatal(cfg)
|
||||
}
|
||||
|
||||
// test etag caching
|
||||
if cache_result, err := Client.GetClientLicenceConfig(result.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(cache_result.Data.(map[string]string)) != 0 {
|
||||
t.Log(cache_result.Data)
|
||||
t.Fatal("cache should be empty")
|
||||
}
|
||||
|
||||
th.App.SetClientLicense(map[string]string{"IsLicensed": "true"})
|
||||
|
||||
if cache_result, err := Client.GetClientLicenceConfig(result.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(cache_result.Data.(map[string]string)) == 0 {
|
||||
t.Fatal("result should not be empty")
|
||||
}
|
||||
|
||||
th.App.SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "true"})
|
||||
|
||||
if cache_result, err := Client.GetClientLicenceConfig(result.Etag); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(cache_result.Data.(map[string]string)) == 0 {
|
||||
t.Fatal("result should not be empty")
|
||||
}
|
||||
|
||||
th.App.SetClientLicense(map[string]string{"IsLicensed": "false"})
|
||||
}
|
||||
}
|
||||
264
api/oauth.go
264
api/oauth.go
@@ -1,264 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitOAuth() {
|
||||
api.BaseRoutes.OAuth.Handle("/register", api.ApiUserRequired(registerOAuthApp)).Methods("POST")
|
||||
api.BaseRoutes.OAuth.Handle("/list", api.ApiUserRequired(getOAuthApps)).Methods("GET")
|
||||
api.BaseRoutes.OAuth.Handle("/app/{client_id}", api.ApiUserRequired(getOAuthAppInfo)).Methods("GET")
|
||||
api.BaseRoutes.OAuth.Handle("/allow", api.ApiUserRequired(allowOAuth)).Methods("GET")
|
||||
api.BaseRoutes.OAuth.Handle("/authorized", api.ApiUserRequired(getAuthorizedApps)).Methods("GET")
|
||||
api.BaseRoutes.OAuth.Handle("/delete", api.ApiUserRequired(deleteOAuthApp)).Methods("POST")
|
||||
api.BaseRoutes.OAuth.Handle("/{id:[A-Za-z0-9]+}/deauthorize", api.ApiUserRequired(deauthorizeOAuthApp)).Methods("POST")
|
||||
api.BaseRoutes.OAuth.Handle("/{id:[A-Za-z0-9]+}/regen_secret", api.ApiUserRequired(regenerateOAuthSecret)).Methods("POST")
|
||||
api.BaseRoutes.OAuth.Handle("/{service:[A-Za-z0-9]+}/login", api.AppHandlerIndependent(loginWithOAuth)).Methods("GET")
|
||||
api.BaseRoutes.OAuth.Handle("/{service:[A-Za-z0-9]+}/signup", api.AppHandlerIndependent(signupWithOAuth)).Methods("GET")
|
||||
}
|
||||
|
||||
func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
|
||||
c.Err = model.NewAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
oauthApp := model.OAuthAppFromJson(r.Body)
|
||||
|
||||
if oauthApp == nil {
|
||||
c.SetInvalidParam("registerOAuthApp", "app")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
oauthApp.IsTrusted = false
|
||||
}
|
||||
|
||||
oauthApp.CreatorId = c.Session.UserId
|
||||
|
||||
rapp, err := c.App.CreateOAuthApp(oauthApp)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("client_id=" + rapp.Id)
|
||||
w.Write([]byte(rapp.ToJson()))
|
||||
}
|
||||
|
||||
func getOAuthApps(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
|
||||
c.Err = model.NewAppError("getOAuthApps", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var apps []*model.OAuthApp
|
||||
var err *model.AppError
|
||||
if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
|
||||
apps, err = c.App.GetOAuthApps(0, 100000)
|
||||
} else {
|
||||
apps, err = c.App.GetOAuthAppsByCreator(c.Session.UserId, 0, 100000)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.OAuthAppListToJson(apps)))
|
||||
}
|
||||
|
||||
func getOAuthAppInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
clientId := params["client_id"]
|
||||
|
||||
oauthApp, err := c.App.GetOAuthApp(clientId)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
oauthApp.Sanitize()
|
||||
w.Write([]byte(oauthApp.ToJson()))
|
||||
}
|
||||
|
||||
func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
responseType := r.URL.Query().Get("response_type")
|
||||
if len(responseType) == 0 {
|
||||
c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_response.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
clientId := r.URL.Query().Get("client_id")
|
||||
if len(clientId) != 26 {
|
||||
c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_client.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
redirectUri := r.URL.Query().Get("redirect_uri")
|
||||
if len(redirectUri) == 0 {
|
||||
c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_redirect.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
scope := r.URL.Query().Get("scope")
|
||||
state := r.URL.Query().Get("state")
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
authRequest := &model.AuthorizeRequest{
|
||||
ResponseType: responseType,
|
||||
ClientId: clientId,
|
||||
RedirectUri: redirectUri,
|
||||
Scope: scope,
|
||||
State: state,
|
||||
}
|
||||
|
||||
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.Session.UserId, authRequest)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
|
||||
w.Write([]byte(model.MapToJson(map[string]string{"redirect": redirectUrl})))
|
||||
}
|
||||
|
||||
func getAuthorizedApps(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
apps, err := c.App.GetAuthorizedAppsForUser(c.Session.UserId, 0, 10000)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.OAuthAppListToJson(apps)))
|
||||
}
|
||||
|
||||
func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
loginHint := r.URL.Query().Get("login_hint")
|
||||
redirectTo := r.URL.Query().Get("redirect_to")
|
||||
|
||||
teamId, err := c.App.GetTeamIdFromQuery(r.URL.Query())
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if authUrl, err := c.App.GetOAuthLoginEndpoint(w, r, service, teamId, model.OAUTH_ACTION_LOGIN, redirectTo, loginHint); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, authUrl, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
|
||||
if !c.App.Config().TeamSettings.EnableUserCreation {
|
||||
c.Err = model.NewAppError("signupWithOAuth", "api.oauth.singup_with_oauth.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
teamId, err := c.App.GetTeamIdFromQuery(r.URL.Query())
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if authUrl, err := c.App.GetOAuthSignupEndpoint(w, r, service, teamId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, authUrl, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteOAuthApp", "id")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
|
||||
c.Err = model.NewAppError("deleteOAuthApp", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
oauthApp, err := c.App.GetOAuthApp(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != oauthApp.CreatorId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewAppError("deleteOAuthApp", "api.oauth.delete.permissions.app_error", nil, "user_id="+c.Session.UserId, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
err = c.App.DeleteOAuthApp(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
err := c.App.DeauthorizeOAuthAppForUser(c.Session.UserId, id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func regenerateOAuthSecret(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
||||
oauthApp, err := c.App.GetOAuthApp(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if oauthApp.CreatorId != c.Session.UserId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
|
||||
c.Err = model.NewAppError("regenerateOAuthSecret", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
oauthApp, err = c.App.RegenerateOAuthAppSecret(oauthApp)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(oauthApp.ToJson()))
|
||||
}
|
||||
@@ -1,895 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestOAuthRegisterApp(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}, IsTrusted: true}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
// calling the endpoint without an app
|
||||
if _, err := Client.DoApiPost("/oauth/register", ""); err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("not logged in - should have failed")
|
||||
}
|
||||
|
||||
th.LoginSystemAdmin()
|
||||
Client = th.SystemAdminClient
|
||||
|
||||
if result, err := Client.RegisterApp(oauthApp); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rapp := result.Data.(*model.OAuthApp)
|
||||
if len(rapp.Id) != 26 {
|
||||
t.Fatal("clientid didn't return properly")
|
||||
}
|
||||
if len(rapp.ClientSecret) != 26 {
|
||||
t.Fatal("client secret didn't return properly")
|
||||
}
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "", Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("missing name - should have failed")
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("missing homepage - should have failed")
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{}}
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("missing callback url - should have failed")
|
||||
}
|
||||
|
||||
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
|
||||
|
||||
ruser := Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
th.App.UpdateUserRoles(ruser.Id, "", false)
|
||||
|
||||
Client.Logout()
|
||||
Client.Login(user.Email, user.Password)
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}, IsTrusted: true}
|
||||
if _, err := Client.RegisterApp(oauthApp); err == nil {
|
||||
t.Fatal("should have failed. not enough permissions")
|
||||
}
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
th.LoginBasic()
|
||||
|
||||
if result, err := th.BasicClient.RegisterApp(oauthApp); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rapp := result.Data.(*model.OAuthApp)
|
||||
if rapp.IsTrusted {
|
||||
t.Fatal("trusted should be false - created by non admin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthAllow(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
state := "123"
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
if result, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
redirect := result.Data.(map[string]string)["redirect"]
|
||||
if len(redirect) == 0 {
|
||||
t.Fatal("redirect url should be set")
|
||||
}
|
||||
|
||||
ru, _ := url.Parse(redirect)
|
||||
if ru == nil {
|
||||
t.Fatal("redirect url unparseable")
|
||||
} else {
|
||||
if len(ru.Query().Get("code")) == 0 {
|
||||
t.Fatal("authorization code not returned")
|
||||
}
|
||||
if ru.Query().Get("state") != state {
|
||||
t.Fatal("returned state doesn't match")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "", "all", state); err == nil {
|
||||
t.Fatal("should have failed - no redirect_url given")
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth("", oauthApp.Id, "", "", state); err == nil {
|
||||
t.Fatal("should have failed - no response type given")
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "", "", state); err == nil {
|
||||
t.Fatal("should have failed - no redirect_url given")
|
||||
}
|
||||
|
||||
if result, err := Client.AllowOAuth("junk", oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
redirect := result.Data.(map[string]string)["redirect"]
|
||||
if len(redirect) == 0 {
|
||||
t.Fatal("redirect url should be set")
|
||||
}
|
||||
|
||||
ru, _ := url.Parse(redirect)
|
||||
if ru == nil {
|
||||
t.Fatal("redirect url unparseable")
|
||||
} else {
|
||||
if ru.Query().Get("error") != "unsupported_response_type" {
|
||||
t.Fatal("wrong error returned")
|
||||
}
|
||||
if ru.Query().Get("state") != state {
|
||||
t.Fatal("returned state doesn't match")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "", oauthApp.CallbackUrls[0], "all", state); err == nil {
|
||||
t.Fatal("should have failed - empty client id")
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "junk", oauthApp.CallbackUrls[0], "all", state); err == nil {
|
||||
t.Fatal("should have failed - bad client id")
|
||||
}
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://somewhereelse.com", "", state); err == nil {
|
||||
t.Fatal("should have failed - redirect uri host does not match app host")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthGetAppsByUser(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := Client.GetOAuthAppsByUser(); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
if _, err := Client.GetOAuthAppsByUser(); err == nil {
|
||||
t.Fatal("Should have failed.")
|
||||
}
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
if result, err := Client.GetOAuthAppsByUser(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
apps := result.Data.([]*model.OAuthApp)
|
||||
|
||||
if len(apps) != 0 {
|
||||
t.Fatal("incorrect number of apps should have been 0")
|
||||
}
|
||||
}
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if result, err := Client.GetOAuthAppsByUser(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
apps := result.Data.([]*model.OAuthApp)
|
||||
|
||||
if len(apps) != 1 {
|
||||
t.Fatal("incorrect number of apps should have been 1")
|
||||
}
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp4" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = AdminClient.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if result, err := AdminClient.GetOAuthAppsByUser(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
apps := result.Data.([]*model.OAuthApp)
|
||||
|
||||
if len(apps) < 4 {
|
||||
t.Fatal("incorrect number of apps should have been 4 or more")
|
||||
}
|
||||
}
|
||||
|
||||
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
|
||||
ruser := Client.Must(AdminClient.CreateUser(user, "")).Data.(*model.User)
|
||||
if _, err := th.App.UpdateUserRoles(ruser.Id, "", false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Login(user.Email, user.Password)
|
||||
|
||||
if _, err := Client.GetOAuthAppsByUser(); err == nil {
|
||||
t.Fatal("should have failed. not enough permissions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthGetAppInfo(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := Client.GetOAuthAppInfo("fakeId"); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := Client.GetOAuthAppInfo(model.NewId()); err == nil {
|
||||
t.Fatal("Should have failed")
|
||||
}
|
||||
|
||||
if _, err := Client.GetOAuthAppInfo(oauthApp.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthGetAuthorizedApps(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := Client.GetOAuthAuthorizedApps(); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://nowhere.com", "user", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if result, err := Client.GetOAuthAuthorizedApps(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
apps := result.Data.([]*model.OAuthApp)
|
||||
|
||||
if len(apps) != 1 {
|
||||
t.Fatal("incorrect number of apps should have been 1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthDeauthorizeApp(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if err := Client.OAuthDeauthorizeApp(model.NewId()); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://nowhere.com", "user", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Client.OAuthDeauthorizeApp(""); err == nil {
|
||||
t.Fatal("Should have failed - no id provided")
|
||||
}
|
||||
|
||||
a1 := model.AccessData{}
|
||||
a1.ClientId = oauthApp.Id
|
||||
a1.UserId = th.BasicUser.Id
|
||||
a1.Token = model.NewId()
|
||||
a1.RefreshToken = model.NewId()
|
||||
a1.ExpiresAt = model.GetMillis()
|
||||
a1.RedirectUri = "http://example.com"
|
||||
<-th.App.Srv.Store.OAuth().SaveAccessData(&a1)
|
||||
|
||||
if err := Client.OAuthDeauthorizeApp(oauthApp.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if result, err := Client.GetOAuthAuthorizedApps(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
apps := result.Data.([]*model.OAuthApp)
|
||||
|
||||
if len(apps) != 0 {
|
||||
t.Fatal("incorrect number of apps should have been 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthRegenerateAppSecret(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := AdminClient.RegenerateOAuthAppSecret(model.NewId()); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp6" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := AdminClient.RegenerateOAuthAppSecret(model.NewId()); err == nil {
|
||||
t.Fatal("Should have failed - invalid app id")
|
||||
}
|
||||
|
||||
if _, err := Client.RegenerateOAuthAppSecret(oauthApp.Id); err == nil {
|
||||
t.Fatal("Should have failed - only admin or the user who registered the app are allowed to perform this action")
|
||||
}
|
||||
|
||||
if regenApp, err := AdminClient.RegenerateOAuthAppSecret(oauthApp.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
app2 := regenApp.Data.(*model.OAuthApp)
|
||||
if app2.Id != oauthApp.Id {
|
||||
t.Fatal("Should have been the same app Id")
|
||||
}
|
||||
|
||||
if app2.ClientSecret == oauthApp.ClientSecret {
|
||||
t.Fatal("Should have been different client Secrets")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthDeleteApp(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
AdminClient := th.SystemAdminClient
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
if !th.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
if _, err := Client.DeleteOAuthApp("fakeId"); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
|
||||
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
|
||||
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := AdminClient.DeleteOAuthApp(""); err == nil {
|
||||
t.Fatal("Should have failed - id not provided")
|
||||
}
|
||||
|
||||
if _, err := AdminClient.DeleteOAuthApp(model.NewId()); err == nil {
|
||||
t.Fatal("Should have failed - invalid id")
|
||||
}
|
||||
|
||||
if _, err := AdminClient.DeleteOAuthApp(oauthApp.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oauthApp = &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err == nil {
|
||||
t.Fatal("Should have failed - only admin or the user who registered the app are allowed to perform this action")
|
||||
}
|
||||
|
||||
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
|
||||
ruser := Client.Must(AdminClient.CreateUser(user, "")).Data.(*model.User)
|
||||
th.App.UpdateUserRoles(ruser.Id, "", false)
|
||||
|
||||
Client.Logout()
|
||||
Client.Login(user.Email, user.Password)
|
||||
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err == nil {
|
||||
t.Fatal("Should have failed - not enough permissions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthAccessToken(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{"12345678901234567890123456"}, "client_secret": []string{"12345678901234567890123456"}, "code": []string{"junk"}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
|
||||
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
redirect := Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ := url.Parse(redirect)
|
||||
|
||||
Client.Logout()
|
||||
|
||||
data = url.Values{"grant_type": []string{"junk"}, "client_id": []string{oauthApp.Id}, "client_secret": []string{oauthApp.ClientSecret}, "code": []string{rurl.Query().Get("code")}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
|
||||
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - bad grant type")
|
||||
}
|
||||
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", "")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - missing client id")
|
||||
}
|
||||
data.Set("client_id", "junk")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - bad client id")
|
||||
}
|
||||
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", "")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - missing client secret")
|
||||
}
|
||||
|
||||
data.Set("client_secret", "junk")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - bad client secret")
|
||||
}
|
||||
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("code", "")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - missing code")
|
||||
}
|
||||
|
||||
data.Set("code", "junk")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - bad code")
|
||||
}
|
||||
|
||||
data.Set("code", rurl.Query().Get("code"))
|
||||
data.Set("redirect_uri", "junk")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - non-matching redirect uri")
|
||||
}
|
||||
|
||||
// reset data for successful request
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("code", rurl.Query().Get("code"))
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
|
||||
token := ""
|
||||
refreshToken := ""
|
||||
if result, err := Client.GetAccessToken(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rsp := result.Data.(*model.AccessResponse)
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else {
|
||||
token = rsp.AccessToken
|
||||
refreshToken = rsp.RefreshToken
|
||||
}
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100?access_token="+token, "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
userMap := model.UserMapFromJson(result.Body)
|
||||
if len(userMap) == 0 {
|
||||
t.Fatal("user map empty - did not get results correctly")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100", "", ""); err == nil {
|
||||
t.Fatal("should have failed - no access token provided")
|
||||
}
|
||||
|
||||
if _, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100?access_token=junk", "", ""); err == nil {
|
||||
t.Fatal("should have failed - bad access token provided")
|
||||
}
|
||||
|
||||
Client.SetOAuthToken(token)
|
||||
if result, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
userMap := model.UserMapFromJson(result.Body)
|
||||
if len(userMap) == 0 {
|
||||
t.Fatal("user map empty - did not get results correctly")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("should have failed - tried to reuse auth code")
|
||||
}
|
||||
|
||||
data.Set("grant_type", model.REFRESH_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("refresh_token", "")
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
data.Del("code")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("Should have failed - refresh token empty")
|
||||
}
|
||||
|
||||
data.Set("refresh_token", refreshToken)
|
||||
if result, err := Client.GetAccessToken(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rsp := result.Data.(*model.AccessResponse)
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else if rsp.RefreshToken == refreshToken {
|
||||
t.Fatal("refresh token did not update")
|
||||
}
|
||||
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
Client.SetOAuthToken(rsp.AccessToken)
|
||||
_, err = Client.GetMe("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data.Set("refresh_token", rsp.RefreshToken)
|
||||
}
|
||||
|
||||
if result, err := Client.GetAccessToken(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rsp := result.Data.(*model.AccessResponse)
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else if rsp.RefreshToken == refreshToken {
|
||||
t.Fatal("refresh token did not update")
|
||||
}
|
||||
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
Client.SetOAuthToken(rsp.AccessToken)
|
||||
_, err = Client.GetMe("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
authData := &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: -1}
|
||||
<-th.App.Srv.Store.OAuth().SaveAuthData(authData)
|
||||
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
data.Set("code", authData.Code)
|
||||
data.Del("refresh_token")
|
||||
if _, err := Client.GetAccessToken(data); err == nil {
|
||||
t.Fatal("Should have failed - code is expired")
|
||||
}
|
||||
|
||||
Client.ClearOAuthToken()
|
||||
}
|
||||
|
||||
func TestOAuthComplete(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
if r, err := HttpGet(Client.Url+"/login/gitlab/complete", Client.HttpClient, "", true); err == nil {
|
||||
t.Fatal("should have failed - no code provided")
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123", Client.HttpClient, "", true); err == nil {
|
||||
t.Fatal("should have failed - gitlab disabled")
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Enable = true })
|
||||
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state=!#$#F@#Yˆ&~ñ", Client.HttpClient, "", true); err == nil {
|
||||
t.Fatal("should have failed - gitlab disabled")
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = model.NewId() })
|
||||
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
stateProps["team_id"] = th.BasicTeam.Id
|
||||
stateProps["redirect_to"] = th.App.Config().GitLabSettings.AuthEndpoint
|
||||
|
||||
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true); err == nil {
|
||||
t.Fatal("should have failed - bad state")
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
stateProps["hash"] = utils.HashSha256(th.App.Config().GitLabSettings.Id)
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true); err == nil {
|
||||
t.Fatal("should have failed - no connection")
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
// We are going to use mattermost as the provider emulating gitlab
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
oauthApp := &model.OAuthApp{
|
||||
Name: "TestApp5" + model.NewId(),
|
||||
Homepage: "https://nowhere.com",
|
||||
Description: "test",
|
||||
CallbackUrls: []string{
|
||||
Client.Url + "/signup/" + model.SERVICE_GITLAB + "/complete",
|
||||
Client.Url + "/login/" + model.SERVICE_GITLAB + "/complete",
|
||||
},
|
||||
IsTrusted: true,
|
||||
}
|
||||
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = oauthApp.Id })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Secret = oauthApp.ClientSecret })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.TokenEndpoint = Client.Url + "/oauth/access_token" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.UserApiEndpoint = Client.ApiUrl + "/users/me" })
|
||||
|
||||
provider := &MattermostTestProvider{}
|
||||
|
||||
redirect := Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ := url.Parse(redirect)
|
||||
code := rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
|
||||
delete(stateProps, "team_id")
|
||||
stateProps["redirect_to"] = th.App.Config().GitLabSettings.AuthEndpoint
|
||||
stateProps["hash"] = utils.HashSha256(th.App.Config().GitLabSettings.Id)
|
||||
stateProps["redirect_to"] = "/oauth/authorize"
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
einterfaces.RegisterOauthProvider(model.SERVICE_GITLAB, provider)
|
||||
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ = url.Parse(redirect)
|
||||
code = rurl.Query().Get("code")
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
if result := <-th.App.Srv.Store.User().UpdateAuthData(
|
||||
th.BasicUser.Id, model.SERVICE_GITLAB, &th.BasicUser.Email, th.BasicUser.Email, true); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
}
|
||||
|
||||
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ = url.Parse(redirect)
|
||||
code = rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ = url.Parse(redirect)
|
||||
code = rurl.Query().Get("code")
|
||||
delete(stateProps, "action")
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||
rurl, _ = url.Parse(redirect)
|
||||
code = rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
}
|
||||
|
||||
func HttpGet(url string, httpClient *http.Client, authToken string, followRedirect bool) (*http.Response, *model.AppError) {
|
||||
rq, _ := http.NewRequest("GET", url, nil)
|
||||
rq.Close = true
|
||||
|
||||
if len(authToken) > 0 {
|
||||
rq.Header.Set(model.HEADER_AUTH, authToken)
|
||||
}
|
||||
|
||||
if !followRedirect {
|
||||
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
}
|
||||
|
||||
if rp, err := httpClient.Do(rq); err != nil {
|
||||
return nil, model.NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
|
||||
} else if rp.StatusCode == 304 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode == 307 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode >= 300 {
|
||||
defer closeBody(rp)
|
||||
return rp, model.AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return rp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r.Body != nil {
|
||||
ioutil.ReadAll(r.Body)
|
||||
r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type MattermostTestProvider struct {
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetIdentifier() string {
|
||||
return model.SERVICE_GITLAB
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader) *model.User {
|
||||
return model.UserFromJson(data)
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetAuthDataFromJson(data io.Reader) string {
|
||||
authData := model.UserFromJson(data)
|
||||
return authData.Email
|
||||
}
|
||||
560
api/post.go
560
api/post.go
@@ -1,560 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
const OPEN_GRAPH_METADATA_CACHE_SIZE = 10000
|
||||
|
||||
var openGraphDataCache = utils.NewLru(OPEN_GRAPH_METADATA_CACHE_SIZE)
|
||||
|
||||
func (api *API) InitPost() {
|
||||
api.BaseRoutes.ApiRoot.Handle("/get_opengraph_metadata", api.ApiUserRequired(getOpenGraphMetadata)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.NeedTeam.Handle("/posts/search", api.ApiUserRequiredActivity(searchPosts, true)).Methods("POST")
|
||||
api.BaseRoutes.NeedTeam.Handle("/posts/flagged/{offset:[0-9]+}/{limit:[0-9]+}", api.ApiUserRequired(getFlaggedPosts)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/posts/{post_id}", api.ApiUserRequired(getPostById)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/pltmp/{post_id}", api.ApiUserRequired(getPermalinkTmp)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Posts.Handle("/create", api.ApiUserRequiredActivity(createPost, true)).Methods("POST")
|
||||
api.BaseRoutes.Posts.Handle("/update", api.ApiUserRequiredActivity(updatePost, true)).Methods("POST")
|
||||
api.BaseRoutes.Posts.Handle("/page/{offset:[0-9]+}/{limit:[0-9]+}", api.ApiUserRequired(getPosts)).Methods("GET")
|
||||
api.BaseRoutes.Posts.Handle("/since/{time:[0-9]+}", api.ApiUserRequired(getPostsSince)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.NeedPost.Handle("/get", api.ApiUserRequired(getPost)).Methods("GET")
|
||||
api.BaseRoutes.NeedPost.Handle("/delete", api.ApiUserRequiredActivity(deletePost, true)).Methods("POST")
|
||||
api.BaseRoutes.NeedPost.Handle("/before/{offset:[0-9]+}/{num_posts:[0-9]+}", api.ApiUserRequired(getPostsBefore)).Methods("GET")
|
||||
api.BaseRoutes.NeedPost.Handle("/after/{offset:[0-9]+}/{num_posts:[0-9]+}", api.ApiUserRequired(getPostsAfter)).Methods("GET")
|
||||
api.BaseRoutes.NeedPost.Handle("/get_file_infos", api.ApiUserRequired(getFileInfosForPost)).Methods("GET")
|
||||
api.BaseRoutes.NeedPost.Handle("/pin", api.ApiUserRequired(pinPost)).Methods("POST")
|
||||
api.BaseRoutes.NeedPost.Handle("/unpin", api.ApiUserRequired(unpinPost)).Methods("POST")
|
||||
}
|
||||
|
||||
func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
post := model.PostFromJson(r.Body)
|
||||
if post == nil {
|
||||
c.SetInvalidParam("createPost", "post")
|
||||
return
|
||||
}
|
||||
|
||||
post.UserId = c.Session.UserId
|
||||
|
||||
hasPermission := false
|
||||
if c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_CREATE_POST) {
|
||||
hasPermission = true
|
||||
} else if channel, err := c.App.GetChannel(post.ChannelId); err == nil {
|
||||
// Temporary permission check method until advanced permissions, please do not copy
|
||||
if channel.Type == model.CHANNEL_OPEN && c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_POST_PUBLIC) {
|
||||
hasPermission = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_POST)
|
||||
return
|
||||
}
|
||||
|
||||
if post.CreateAt != 0 && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
post.CreateAt = 0
|
||||
}
|
||||
|
||||
rp, err := c.App.CreatePostAsUser(post)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(rp.ToJson()))
|
||||
}
|
||||
|
||||
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
post := model.PostFromJson(r.Body)
|
||||
|
||||
if post == nil {
|
||||
c.SetInvalidParam("updatePost", "post")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_EDIT_POST) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_POST)
|
||||
return
|
||||
}
|
||||
|
||||
post.UserId = c.Session.UserId
|
||||
|
||||
rpost, err := c.App.UpdatePost(post, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(rpost.ToJson()))
|
||||
}
|
||||
|
||||
func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("savedIsPinnedPost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("savedIsPinnedPost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
pchan := c.App.Srv.Store.Post().Get(postId)
|
||||
|
||||
var oldPost *model.Post
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
oldPost = result.Data.(*model.PostList).Posts[postId]
|
||||
newPost := &model.Post{}
|
||||
*newPost = *oldPost
|
||||
newPost.IsPinned = isPinned
|
||||
|
||||
if result := <-c.App.Srv.Store.Post().Update(newPost, oldPost); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
rpost := result.Data.(*model.Post)
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil)
|
||||
message.Add("post", c.App.PostWithProxyAddedToImageURLs(rpost).ToJson())
|
||||
c.App.Publish(message)
|
||||
|
||||
c.App.InvalidateCacheForChannelPosts(rpost.ChannelId)
|
||||
|
||||
w.Write([]byte(rpost.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pinPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
saveIsPinnedPost(c, w, r, true)
|
||||
}
|
||||
|
||||
func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
saveIsPinnedPost(c, w, r, false)
|
||||
}
|
||||
|
||||
func getFlaggedPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getFlaggedPosts", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(params["limit"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getFlaggedPosts", "limit")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_VIEW_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
|
||||
return
|
||||
}
|
||||
|
||||
if posts, err := c.App.GetFlaggedPostsForTeam(c.Session.UserId, c.TeamId, offset, limit); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(posts.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["channel_id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("getPosts", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getPosts", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(params["limit"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getPosts", "limit")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, id, model.PERMISSION_CREATE_POST) {
|
||||
c.SetPermissionError(model.PERMISSION_CREATE_POST)
|
||||
return
|
||||
}
|
||||
|
||||
etag := c.App.GetPostsEtag(id)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPosts(id, offset, limit); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["channel_id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("getPostsSince", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
time, err := strconv.ParseInt(params["time"], 10, 64)
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getPostsSince", "time")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPostsSince(id, time); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("getPost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPostThread(postId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(list.Etag(), "Get Post", w, r) {
|
||||
return
|
||||
} else {
|
||||
if !list.IsChannelId(channelId) {
|
||||
c.Err = model.NewAppError("getPost", "api.post.get_post.permissions.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPostById(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPostById", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPostThread(postId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if len(list.Order) != 1 {
|
||||
c.Err = model.NewAppError("getPostById", "api.post_get_post_by_id.get.app_error", nil, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
post := list.Posts[list.Order[0]]
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if c.HandleEtag(list.Etag(), "Get Post By Id", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPermalinkTmp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPermalinkTmp", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
if result := <-c.App.Srv.Store.Channel().GetForPost(postId); result.Err == nil {
|
||||
channel = result.Data.(*model.Channel)
|
||||
} else {
|
||||
c.SetInvalidParam("getPermalinkTmp", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type == model.CHANNEL_OPEN {
|
||||
if !c.App.HasPermissionToChannelByPost(c.Session.UserId, postId, model.PERMISSION_JOIN_PUBLIC_CHANNELS) {
|
||||
c.SetPermissionError(model.PERMISSION_JOIN_PUBLIC_CHANNELS)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !c.App.HasPermissionToChannelByPost(c.Session.UserId, postId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPermalinkPost(postId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(list.Etag(), "Get Permalink TMP", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_DELETE_POST) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_POST)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToPost(c.Session, postId, model.PERMISSION_DELETE_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS)
|
||||
return
|
||||
}
|
||||
|
||||
if post, err := c.App.DeletePost(postId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if post.ChannelId != channelId {
|
||||
c.Err = model.NewAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
result["id"] = postId
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func getPostsBefore(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
getPostsBeforeOrAfter(c, w, r, true)
|
||||
}
|
||||
|
||||
func getPostsAfter(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
getPostsBeforeOrAfter(c, w, r, false)
|
||||
}
|
||||
|
||||
func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, before bool) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["channel_id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
numPosts, err := strconv.Atoi(params["num_posts"])
|
||||
if err != nil || numPosts <= 0 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "numPosts")
|
||||
return
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil || offset < 0 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
// We can do better than this etag in this situation
|
||||
etag := c.App.GetPostsEtag(id)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts Before or After", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
if list, err := c.App.GetPostsAroundPost(postId, id, offset, numPosts, before); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.StringInterfaceFromJson(r.Body)
|
||||
|
||||
terms := props["terms"].(string)
|
||||
if len(terms) == 0 {
|
||||
c.SetInvalidParam("search", "terms")
|
||||
return
|
||||
}
|
||||
|
||||
isOrSearch := false
|
||||
if val, ok := props["is_or_search"]; ok && val != nil {
|
||||
isOrSearch = val.(bool)
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
posts, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.TeamId, isOrSearch)
|
||||
|
||||
elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
|
||||
metrics := c.App.Metrics
|
||||
if metrics != nil {
|
||||
metrics.IncrementPostsSearchCounter()
|
||||
metrics.ObservePostsSearchDuration(elapsedTime)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Write([]byte(posts.ToJson()))
|
||||
}
|
||||
|
||||
func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("getFileInfosForPost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getFileInfosForPost", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if infos, err := c.App.GetFileInfosForPost(postId, false); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) {
|
||||
return
|
||||
} else {
|
||||
if len(infos) > 0 {
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos))
|
||||
w.Write([]byte(model.FileInfosToJson(infos)))
|
||||
}
|
||||
}
|
||||
|
||||
func getOpenGraphMetadata(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*c.App.Config().ServiceSettings.EnableLinkPreviews {
|
||||
c.Err = model.NewAppError("getOpenGraphMetadata", "api.post.link_preview_disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
props := model.StringInterfaceFromJson(r.Body)
|
||||
|
||||
ogJSONGeneric, ok := openGraphDataCache.Get(props["url"])
|
||||
if ok {
|
||||
w.Write(ogJSONGeneric.([]byte))
|
||||
return
|
||||
}
|
||||
|
||||
url := ""
|
||||
ok = false
|
||||
if url, ok = props["url"].(string); len(url) == 0 || !ok {
|
||||
c.SetInvalidParam("getOpenGraphMetadata", "url")
|
||||
return
|
||||
}
|
||||
|
||||
og := c.App.GetOpenGraphMetadata(url)
|
||||
|
||||
ogJSON, err := og.ToJSON()
|
||||
openGraphDataCache.AddWithExpiresInSecs(props["url"], ogJSON, 3600) // Cache would expire after 1 hour
|
||||
if err != nil {
|
||||
w.Write([]byte(`{"url": ""}`))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(ogJSON)
|
||||
}
|
||||
1510
api/post_test.go
1510
api/post_test.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitPreference() {
|
||||
api.BaseRoutes.Preferences.Handle("/", api.ApiUserRequired(getAllPreferences)).Methods("GET")
|
||||
api.BaseRoutes.Preferences.Handle("/save", api.ApiUserRequired(savePreferences)).Methods("POST")
|
||||
api.BaseRoutes.Preferences.Handle("/delete", api.ApiUserRequired(deletePreferences)).Methods("POST")
|
||||
api.BaseRoutes.Preferences.Handle("/{category:[A-Za-z0-9_]+}", api.ApiUserRequired(getPreferenceCategory)).Methods("GET")
|
||||
api.BaseRoutes.Preferences.Handle("/{category:[A-Za-z0-9_]+}/{name:[A-Za-z0-9_]+}", api.ApiUserRequired(getPreference)).Methods("GET")
|
||||
}
|
||||
|
||||
func getAllPreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if result := <-c.App.Srv.Store.Preference().GetAll(c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
} else {
|
||||
data := result.Data.(model.Preferences)
|
||||
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func savePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
preferences, err := model.PreferencesFromJson(r.Body)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("savePreferences", "api.preference.save_preferences.decode.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.UpdatePreferences(c.Session.UserId, preferences); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte("true"))
|
||||
}
|
||||
|
||||
func getPreferenceCategory(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
category := params["category"]
|
||||
|
||||
if result := <-c.App.Srv.Store.Preference().GetCategory(c.Session.UserId, category); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
} else {
|
||||
data := result.Data.(model.Preferences)
|
||||
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getPreference(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
category := params["category"]
|
||||
name := params["name"]
|
||||
|
||||
if result := <-c.App.Srv.Store.Preference().Get(c.Session.UserId, category, name); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
} else {
|
||||
data := result.Data.(model.Preference)
|
||||
w.Write([]byte(data.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deletePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
preferences, err := model.PreferencesFromJson(r.Body)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("savePreferences", "api.preference.delete_preferences.decode.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeletePreferences(c.Session.UserId, preferences); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestGetAllPreferences(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user1 := th.BasicUser
|
||||
|
||||
category := model.NewId()
|
||||
|
||||
preferences1 := model.Preferences{
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: category,
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: category,
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
},
|
||||
}
|
||||
|
||||
Client.Must(Client.SetPreferences(&preferences1))
|
||||
|
||||
if result, err := Client.GetAllPreferences(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(model.Preferences); len(data) != 4 {
|
||||
t.Fatal("received the wrong number of preferences")
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
|
||||
if result, err := Client.GetAllPreferences(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(model.Preferences); len(data) == 0 {
|
||||
t.Fatal("received the wrong number of preferences")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPreferences(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user1 := th.BasicUser
|
||||
|
||||
// save 10 preferences
|
||||
var preferences model.Preferences
|
||||
for i := 0; i < 10; i++ {
|
||||
preference := model.Preference{
|
||||
UserId: user1.Id,
|
||||
Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
preferences = append(preferences, preference)
|
||||
}
|
||||
|
||||
if _, err := Client.SetPreferences(&preferences); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// update 10 preferences
|
||||
for _, preference := range preferences {
|
||||
preference.Value = "1234garbage"
|
||||
}
|
||||
|
||||
if _, err := Client.SetPreferences(&preferences); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.SetPreferences(&preferences); err == nil {
|
||||
t.Fatal("shouldn't have been able to update another user's preferences")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPreferenceCategory(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user1 := th.BasicUser
|
||||
|
||||
category := model.NewId()
|
||||
|
||||
preferences1 := model.Preferences{
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: category,
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: category,
|
||||
Name: model.NewId(),
|
||||
},
|
||||
{
|
||||
UserId: user1.Id,
|
||||
Category: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
},
|
||||
}
|
||||
|
||||
Client.Must(Client.SetPreferences(&preferences1))
|
||||
|
||||
if result, err := Client.GetPreferenceCategory(category); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(model.Preferences); len(data) != 2 {
|
||||
t.Fatal("received the wrong number of preferences")
|
||||
} else if !((data[0] == preferences1[0] && data[1] == preferences1[1]) || (data[0] == preferences1[1] && data[1] == preferences1[0])) {
|
||||
t.Fatal("received incorrect preferences")
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
|
||||
if result, err := Client.GetPreferenceCategory(category); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(model.Preferences); len(data) != 0 {
|
||||
t.Fatal("received the wrong number of preferences")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPreference(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user := th.BasicUser
|
||||
|
||||
preferences := model.Preferences{
|
||||
{
|
||||
UserId: user.Id,
|
||||
Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
Name: model.NewId(),
|
||||
Value: model.NewId(),
|
||||
},
|
||||
}
|
||||
|
||||
Client.Must(Client.SetPreferences(&preferences))
|
||||
|
||||
if result, err := Client.GetPreference(preferences[0].Category, preferences[0].Name); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(*model.Preference); *data != preferences[0] {
|
||||
t.Fatal("preference saved incorrectly")
|
||||
}
|
||||
|
||||
preferences[0].Value = model.NewId()
|
||||
Client.Must(Client.SetPreferences(&preferences))
|
||||
|
||||
if result, err := Client.GetPreference(preferences[0].Category, preferences[0].Name); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(*model.Preference); *data != preferences[0] {
|
||||
t.Fatal("preference updated incorrectly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePreferences(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user1 := th.BasicUser
|
||||
|
||||
var originalCount int
|
||||
if result, err := Client.GetAllPreferences(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
originalCount = len(result.Data.(model.Preferences))
|
||||
}
|
||||
|
||||
// save 10 preferences
|
||||
var preferences model.Preferences
|
||||
for i := 0; i < 10; i++ {
|
||||
preference := model.Preference{
|
||||
UserId: user1.Id,
|
||||
Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
preferences = append(preferences, preference)
|
||||
}
|
||||
|
||||
if _, err := Client.SetPreferences(&preferences); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// delete 10 preferences
|
||||
th.LoginBasic2()
|
||||
|
||||
if _, err := Client.DeletePreferences(&preferences); err == nil {
|
||||
t.Fatal("shouldn't have been able to delete another user's preferences")
|
||||
}
|
||||
|
||||
th.LoginBasic()
|
||||
if _, err := Client.DeletePreferences(&preferences); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if result, err := Client.GetAllPreferences(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if data := result.Data.(model.Preferences); len(data) != originalCount {
|
||||
t.Fatal("should've deleted preferences")
|
||||
}
|
||||
}
|
||||
149
api/reaction.go
149
api/reaction.go
@@ -1,149 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitReaction() {
|
||||
api.BaseRoutes.NeedPost.Handle("/reactions/save", api.ApiUserRequired(saveReaction)).Methods("POST")
|
||||
api.BaseRoutes.NeedPost.Handle("/reactions/delete", api.ApiUserRequired(deleteReaction)).Methods("POST")
|
||||
api.BaseRoutes.NeedPost.Handle("/reactions", api.ApiUserRequired(listReactions)).Methods("GET")
|
||||
}
|
||||
|
||||
func saveReaction(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
reaction := model.ReactionFromJson(r.Body)
|
||||
if reaction == nil {
|
||||
c.SetInvalidParam("saveReaction", "reaction")
|
||||
return
|
||||
}
|
||||
|
||||
if reaction.UserId != c.Session.UserId {
|
||||
c.Err = model.NewAppError("saveReaction", "api.reaction.save_reaction.user_id.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("saveReaction", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_ADD_REACTION) {
|
||||
c.SetPermissionError(model.PERMISSION_ADD_REACTION)
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 || postId != reaction.PostId {
|
||||
c.SetInvalidParam("saveReaction", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
var post *model.Post
|
||||
|
||||
if result := <-c.App.Srv.Store.Post().Get(reaction.PostId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if post = result.Data.(*model.PostList).Posts[postId]; post.ChannelId != channelId {
|
||||
c.Err = model.NewAppError("saveReaction", "api.reaction.save_reaction.mismatched_channel_id.app_error",
|
||||
nil, "channelId="+channelId+", post.ChannelId="+post.ChannelId+", postId="+postId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if reaction, err := c.App.SaveReactionForPost(reaction); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(reaction.ToJson()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func deleteReaction(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
reaction := model.ReactionFromJson(r.Body)
|
||||
if reaction == nil {
|
||||
c.SetInvalidParam("deleteReaction", "reaction")
|
||||
return
|
||||
}
|
||||
|
||||
if reaction.UserId != c.Session.UserId {
|
||||
c.Err = model.NewAppError("deleteReaction", "api.reaction.delete_reaction.user_id.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deleteReaction", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_REMOVE_REACTION) {
|
||||
c.SetPermissionError(model.PERMISSION_REMOVE_REACTION)
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 || postId != reaction.PostId {
|
||||
c.SetInvalidParam("deleteReaction", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.DeleteReactionForPost(reaction)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func listReactions(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
channelId := params["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("deletePost", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("listReactions", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
pchan := c.App.Srv.Store.Post().Get(postId)
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else if post := result.Data.(*model.PostList).Posts[postId]; post.ChannelId != channelId {
|
||||
c.Err = model.NewAppError("listReactions", "api.reaction.list_reactions.mismatched_channel_id.app_error",
|
||||
nil, "channelId="+channelId+", post.ChannelId="+post.ChannelId+", postId="+postId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-c.App.Srv.Store.Reaction().GetForPost(postId, true); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
reactions := result.Data.([]*model.Reaction)
|
||||
|
||||
w.Write([]byte(model.ReactionsToJson(reactions)))
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestSaveReaction(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
post := th.BasicPost
|
||||
|
||||
// saving a reaction
|
||||
reaction := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(channel.Id, reaction); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction {
|
||||
t.Fatal("didn't save reaction correctly")
|
||||
}
|
||||
|
||||
// saving a duplicate reaction
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// saving a second reaction on a post
|
||||
reaction2 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(channel.Id, reaction2); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction2 = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 2 ||
|
||||
(*reactions[0] != *reaction && *reactions[1] != *reaction) || (*reactions[0] != *reaction2 && *reactions[1] != *reaction2) {
|
||||
t.Fatal("didn't save multiple reactions correctly")
|
||||
}
|
||||
|
||||
// saving a reaction without a user id
|
||||
reaction3 := &model.Reaction{
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction3); err == nil {
|
||||
t.Fatal("should've failed to save reaction without user id")
|
||||
}
|
||||
|
||||
// saving a reaction without a post id
|
||||
reaction4 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction4); err == nil {
|
||||
t.Fatal("should've failed to save reaction without post id")
|
||||
}
|
||||
|
||||
// saving a reaction without a emoji name
|
||||
reaction5 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction5); err == nil {
|
||||
t.Fatal("should've failed to save reaction without emoji name")
|
||||
}
|
||||
|
||||
// saving a reaction for another user
|
||||
reaction6 := &model.Reaction{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction6); err == nil {
|
||||
t.Fatal("should've failed to save reaction for another user")
|
||||
}
|
||||
|
||||
// saving a reaction to a channel we're not a member of
|
||||
th.LoginBasic2()
|
||||
channel2 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
post2 := th.CreatePost(th.BasicClient, channel2)
|
||||
th.LoginBasic()
|
||||
|
||||
reaction7 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel2.Id, reaction7); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a channel we're not a member of")
|
||||
}
|
||||
|
||||
// saving a reaction to a direct channel
|
||||
directChannel := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel)
|
||||
directPost := th.CreatePost(th.BasicClient, directChannel)
|
||||
|
||||
reaction8 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: directPost.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
if returned, err := Client.SaveReaction(directChannel.Id, reaction8); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
reaction8 = returned
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(directChannel.Id, directPost.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction8 {
|
||||
t.Fatal("didn't save reaction correctly")
|
||||
}
|
||||
|
||||
// saving a reaction for a post in the wrong channel
|
||||
reaction9 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: directPost.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel.Id, reaction9); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a post that isn't in the given channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteReaction(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
post := th.BasicPost
|
||||
|
||||
reaction1 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
// deleting a reaction that does exist
|
||||
Client.MustGeneric(Client.SaveReaction(channel.Id, reaction1))
|
||||
if err := Client.DeleteReaction(channel.Id, reaction1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 0 {
|
||||
t.Fatal("should've deleted reaction")
|
||||
}
|
||||
|
||||
// deleting one reaction when a post has multiple
|
||||
reaction2 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
}
|
||||
reaction1 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction1)).(*model.Reaction)
|
||||
reaction2 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction2)).(*model.Reaction)
|
||||
if err := Client.DeleteReaction(channel.Id, reaction2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reactions := Client.MustGeneric(Client.ListReactions(channel.Id, post.Id)).([]*model.Reaction); len(reactions) != 1 || *reactions[0] != *reaction1 {
|
||||
t.Fatal("should've deleted only one reaction")
|
||||
}
|
||||
|
||||
// deleting a reaction made by another user
|
||||
reaction3 := &model.Reaction{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
reaction3 = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction3)).(*model.Reaction)
|
||||
|
||||
th.LoginBasic()
|
||||
if err := Client.DeleteReaction(channel.Id, reaction3); err == nil {
|
||||
t.Fatal("should've failed to delete another user's reaction")
|
||||
}
|
||||
|
||||
// deleting a reaction for a post we can't see
|
||||
channel2 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
post2 := th.CreatePost(th.BasicClient, channel2)
|
||||
|
||||
reaction4 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
}
|
||||
|
||||
reaction4 = Client.MustGeneric(Client.SaveReaction(channel2.Id, reaction4)).(*model.Reaction)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
if err := Client.DeleteReaction(channel2.Id, reaction4); err == nil {
|
||||
t.Fatal("should've failed to delete a reaction from a channel we're not in")
|
||||
}
|
||||
|
||||
// deleting a reaction for a post with the wrong channel
|
||||
channel3 := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
|
||||
reaction5 := &model.Reaction{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "happy",
|
||||
}
|
||||
if _, err := Client.SaveReaction(channel3.Id, reaction5); err == nil {
|
||||
t.Fatal("should've failed to save reaction to a post that isn't in the given channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReactions(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
user := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel := th.BasicChannel
|
||||
|
||||
post := th.BasicPost
|
||||
|
||||
userReactions := []*model.Reaction{
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: user.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
}
|
||||
|
||||
for i, reaction := range userReactions {
|
||||
userReactions[i] = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction)).(*model.Reaction)
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
Client.Must(Client.JoinChannel(channel.Id))
|
||||
|
||||
userReactions2 := []*model.Reaction{
|
||||
{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: user2.Id,
|
||||
PostId: post.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
}
|
||||
|
||||
for i, reaction := range userReactions2 {
|
||||
userReactions2[i] = Client.MustGeneric(Client.SaveReaction(channel.Id, reaction)).(*model.Reaction)
|
||||
}
|
||||
|
||||
if reactions, err := Client.ListReactions(channel.Id, post.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(reactions) != 5 {
|
||||
t.Fatal("should've returned 5 reactions")
|
||||
} else {
|
||||
checkForReaction := func(expected *model.Reaction) {
|
||||
found := false
|
||||
|
||||
for _, reaction := range reactions {
|
||||
if *reaction == *expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("didn't return expected reaction %v", *expected)
|
||||
}
|
||||
}
|
||||
|
||||
for _, reaction := range userReactions {
|
||||
checkForReaction(reaction)
|
||||
}
|
||||
|
||||
for _, reaction := range userReactions2 {
|
||||
checkForReaction(reaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitStatus() {
|
||||
api.BaseRoutes.Users.Handle("/status", api.ApiUserRequired(getStatusesHttp)).Methods("GET")
|
||||
api.BaseRoutes.Users.Handle("/status/ids", api.ApiUserRequired(getStatusesByIdsHttp)).Methods("POST")
|
||||
}
|
||||
|
||||
func getStatusesHttp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
statusMap := model.StatusMapToInterfaceMap(c.App.GetAllStatuses())
|
||||
w.Write([]byte(model.StringInterfaceToJson(statusMap)))
|
||||
}
|
||||
|
||||
func getStatusesByIdsHttp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
userIds := model.ArrayFromJson(r.Body)
|
||||
|
||||
if len(userIds) == 0 {
|
||||
c.SetInvalidParam("getStatusesByIdsHttp", "user_ids")
|
||||
return
|
||||
}
|
||||
|
||||
statusMap, err := c.App.GetStatusesByIds(userIds)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.StringInterfaceToJson(statusMap)))
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
func TestStatuses(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
|
||||
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||
th.LinkUserToTeam(ruser, rteam.Data.(*model.Team))
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
|
||||
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
|
||||
ruser2 := Client.Must(Client.CreateUser(&user2, "")).Data.(*model.User)
|
||||
th.LinkUserToTeam(ruser2, rteam.Data.(*model.Team))
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(ruser2.Id))
|
||||
|
||||
Client.Login(user.Email, user.Password)
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
r1, err := Client.GetStatuses()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
statuses := r1.Data.(map[string]string)
|
||||
|
||||
for _, status := range statuses {
|
||||
if status != model.STATUS_OFFLINE && status != model.STATUS_AWAY && status != model.STATUS_ONLINE {
|
||||
t.Fatal("one of the statuses had an invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
th.LoginBasic2()
|
||||
|
||||
WebSocketClient2, err2 := th.CreateWebSocketClient()
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
}
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
WebSocketClient.GetStatuses()
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
for _, status := range resp.Data {
|
||||
if status != model.STATUS_OFFLINE && status != model.STATUS_AWAY && status != model.STATUS_ONLINE {
|
||||
t.Fatal("one of the statuses had an invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
if status, ok := resp.Data[th.BasicUser2.Id]; !ok {
|
||||
t.Log(resp.Data)
|
||||
t.Fatal("should have had user status")
|
||||
} else if status != model.STATUS_ONLINE {
|
||||
t.Log(status)
|
||||
t.Fatal("status should have been online")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{th.BasicUser2.Id})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
for _, status := range resp.Data {
|
||||
if status != model.STATUS_OFFLINE && status != model.STATUS_AWAY && status != model.STATUS_ONLINE {
|
||||
t.Fatal("one of the statuses had an invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
if status, ok := resp.Data[th.BasicUser2.Id]; !ok {
|
||||
t.Log(len(resp.Data))
|
||||
t.Fatal("should have had user status")
|
||||
} else if status != model.STATUS_ONLINE {
|
||||
t.Log(status)
|
||||
t.Fatal("status should have been online")
|
||||
} else if len(resp.Data) != 1 {
|
||||
t.Fatal("only 1 status should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{ruser2.Id, "junk"})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
if len(resp.Data) != 2 {
|
||||
t.Fatal("2 statuses should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error == nil {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
t.Fatal("should have errored - empty user ids")
|
||||
}
|
||||
|
||||
WebSocketClient2.Close()
|
||||
|
||||
th.App.SetStatusAwayIfNeeded(th.BasicUser.Id, false)
|
||||
|
||||
awayTimeout := *th.App.Config().TeamSettings.UserStatusAwayTimeout
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.UserStatusAwayTimeout = awayTimeout })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.UserStatusAwayTimeout = 1 })
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
th.App.SetStatusAwayIfNeeded(th.BasicUser.Id, false)
|
||||
th.App.SetStatusOnline(th.BasicUser.Id, "junk", false)
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
WebSocketClient.GetStatuses()
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
if _, ok := resp.Data[th.BasicUser2.Id]; ok {
|
||||
t.Fatal("should not have had user status")
|
||||
}
|
||||
}
|
||||
|
||||
stop := make(chan bool)
|
||||
onlineHit := false
|
||||
awayHit := false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_STATUS_CHANGE && resp.Data["user_id"].(string) == th.BasicUser.Id {
|
||||
status := resp.Data["status"].(string)
|
||||
if status == model.STATUS_ONLINE {
|
||||
onlineHit = true
|
||||
} else if status == model.STATUS_AWAY {
|
||||
awayHit = true
|
||||
}
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if !onlineHit {
|
||||
t.Fatal("didn't get online event")
|
||||
}
|
||||
if !awayHit {
|
||||
t.Fatal("didn't get away event")
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
WebSocketClient.Close()
|
||||
}
|
||||
|
||||
func TestGetStatusesByIds(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
|
||||
if result, err := Client.GetStatusesByIds([]string{th.BasicUser.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
statuses := result.Data.(map[string]string)
|
||||
if len(statuses) != 1 {
|
||||
t.Fatal("should only have 1 status")
|
||||
}
|
||||
}
|
||||
|
||||
if result, err := Client.GetStatusesByIds([]string{th.BasicUser.Id, th.BasicUser2.Id, "junk"}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
statuses := result.Data.(map[string]string)
|
||||
if len(statuses) != 3 {
|
||||
t.Fatal("should have 3 statuses")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.GetStatusesByIds([]string{}); err == nil {
|
||||
t.Fatal("should have errored")
|
||||
}
|
||||
}
|
||||
543
api/team.go
543
api/team.go
@@ -1,543 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitTeam() {
|
||||
api.BaseRoutes.Teams.Handle("/create", api.ApiUserRequired(createTeam)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/all", api.ApiUserRequired(getAll)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/all_team_listings", api.ApiUserRequired(GetAllTeamListings)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/get_invite_info", api.ApiAppHandler(getInviteInfo)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/find_team_by_name", api.ApiUserRequired(findTeamByName)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/name/{team_name:[A-Za-z0-9\\-]+}", api.ApiUserRequired(getTeamByName)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/members", api.ApiUserRequired(getMyTeamMembers)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/unread", api.ApiUserRequired(getMyTeamsUnread)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.NeedTeam.Handle("/me", api.ApiUserRequired(getMyTeam)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/stats", api.ApiUserRequired(getTeamStats)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/members/{offset:[0-9]+}/{limit:[0-9]+}", api.ApiUserRequired(getTeamMembers)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/members/ids", api.ApiUserRequired(getTeamMembersByIds)).Methods("POST")
|
||||
api.BaseRoutes.NeedTeam.Handle("/members/{user_id:[A-Za-z0-9]+}", api.ApiUserRequired(getTeamMember)).Methods("GET")
|
||||
api.BaseRoutes.NeedTeam.Handle("/update", api.ApiUserRequired(updateTeam)).Methods("POST")
|
||||
api.BaseRoutes.NeedTeam.Handle("/update_member_roles", api.ApiUserRequired(updateMemberRoles)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.NeedTeam.Handle("/invite_members", api.ApiUserRequired(inviteMembers)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.NeedTeam.Handle("/add_user_to_team", api.ApiUserRequired(addUserToTeam)).Methods("POST")
|
||||
api.BaseRoutes.NeedTeam.Handle("/remove_user_from_team", api.ApiUserRequired(removeUserFromTeam)).Methods("POST")
|
||||
|
||||
// These should be moved to the global admin console
|
||||
api.BaseRoutes.NeedTeam.Handle("/import_team", api.ApiUserRequired(importTeam)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/add_user_to_team_from_invite", api.ApiUserRequiredMfa(addUserToTeamFromInvite)).Methods("POST")
|
||||
}
|
||||
|
||||
func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
team := model.TeamFromJson(r.Body)
|
||||
|
||||
if team == nil {
|
||||
c.SetInvalidParam("createTeam", "team")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_CREATE_TEAM) {
|
||||
c.Err = model.NewAppError("createTeam", "api.team.is_team_creation_allowed.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
rteam, err := c.App.CreateTeamWithUser(team, c.Session.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
// Don't sanitize the team here since the user will be a team admin and their session won't reflect that yet
|
||||
|
||||
w.Write([]byte(rteam.ToJson()))
|
||||
}
|
||||
|
||||
func GetAllTeamListings(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
var teams []*model.Team
|
||||
var err *model.AppError
|
||||
|
||||
if teams, err = c.App.GetAllOpenTeams(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
m := make(map[string]*model.Team)
|
||||
for _, v := range teams {
|
||||
m[v.Id] = v
|
||||
}
|
||||
|
||||
sanitizeTeamMap(c, m)
|
||||
|
||||
w.Write([]byte(model.TeamMapToJson(m)))
|
||||
}
|
||||
|
||||
// Gets all teams which the current user can has access to. If the user is a System Admin, this will be all teams
|
||||
// on the server. Otherwise, it will only be the teams of which the user is a member.
|
||||
func getAll(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
var teams []*model.Team
|
||||
var err *model.AppError
|
||||
|
||||
if c.App.HasPermissionTo(c.Session.UserId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
teams, err = c.App.GetAllTeams()
|
||||
} else {
|
||||
teams, err = c.App.GetTeamsForUser(c.Session.UserId)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
m := make(map[string]*model.Team)
|
||||
for _, v := range teams {
|
||||
m[v.Id] = v
|
||||
}
|
||||
|
||||
sanitizeTeamMap(c, m)
|
||||
|
||||
w.Write([]byte(model.TeamMapToJson(m)))
|
||||
}
|
||||
|
||||
func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
invites := model.InvitesFromJson(r.Body)
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_INVITE_USER) {
|
||||
c.SetPermissionError(model.PERMISSION_INVITE_USER)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_INVITE_USER)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.InviteNewUsersToTeam(invites.ToEmailList(), c.TeamId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(invites.ToJson()))
|
||||
}
|
||||
|
||||
func addUserToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := model.MapFromJson(r.Body)
|
||||
userId := params["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("addUserToTeam", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_ADD_USER_TO_TEAM)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.AddUserToTeam(c.TeamId, userId, ""); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(params)))
|
||||
}
|
||||
|
||||
func removeUserFromTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := model.MapFromJson(r.Body)
|
||||
userId := params["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("removeUserFromTeam", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != userId {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_REMOVE_USER_FROM_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_REMOVE_USER_FROM_TEAM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.App.RemoveUserFromTeam(c.TeamId, userId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(params)))
|
||||
}
|
||||
|
||||
func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := model.MapFromJson(r.Body)
|
||||
tokenId := params["token"]
|
||||
inviteId := params["invite_id"]
|
||||
|
||||
var team *model.Team
|
||||
var err *model.AppError
|
||||
|
||||
if len(tokenId) > 0 {
|
||||
team, err = c.App.AddUserToTeamByToken(c.Session.UserId, tokenId)
|
||||
} else if len(inviteId) > 0 {
|
||||
team, err = c.App.AddUserToTeamByInviteId(inviteId, c.Session.UserId)
|
||||
} else {
|
||||
c.Err = model.NewAppError("addUserToTeamFromInvite", "api.user.create_user.signup_link_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.App.SanitizeTeam(c.Session, team)
|
||||
|
||||
w.Write([]byte(team.ToJson()))
|
||||
}
|
||||
|
||||
func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
name := strings.ToLower(strings.TrimSpace(m["name"]))
|
||||
|
||||
found := c.App.FindTeamByName(name)
|
||||
|
||||
if found {
|
||||
w.Write([]byte("true"))
|
||||
} else {
|
||||
w.Write([]byte("false"))
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
teamname := params["team_name"]
|
||||
|
||||
if team, err := c.App.GetTeamByName(teamname); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if (!team.AllowOpenInvite || team.Type != model.TEAM_OPEN) && c.Session.GetTeamByTeamId(team.Id) == nil {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.App.SanitizeTeam(c.Session, team)
|
||||
|
||||
w.Write([]byte(team.ToJson()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getMyTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if len(c.Session.TeamMembers) > 0 {
|
||||
w.Write([]byte(model.TeamMembersToJson(c.Session.TeamMembers)))
|
||||
} else {
|
||||
if members, err := c.App.GetTeamMembersForUser(c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.TeamMembersToJson(members)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMyTeamsUnread(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
teamId := r.URL.Query().Get("id")
|
||||
|
||||
if unreads, err := c.App.GetTeamsUnreadForUser(teamId, c.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.TeamsUnreadToJson(unreads)))
|
||||
}
|
||||
}
|
||||
|
||||
func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
team := model.TeamFromJson(r.Body)
|
||||
if team == nil {
|
||||
c.SetInvalidParam("updateTeam", "team")
|
||||
return
|
||||
}
|
||||
|
||||
team.Id = c.TeamId
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, team.Id, model.PERMISSION_MANAGE_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
|
||||
return
|
||||
}
|
||||
|
||||
var err *model.AppError
|
||||
var updatedTeam *model.Team
|
||||
|
||||
updatedTeam, err = c.App.UpdateTeam(team)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.App.SanitizeTeam(c.Session, updatedTeam)
|
||||
|
||||
w.Write([]byte(updatedTeam.ToJson()))
|
||||
}
|
||||
|
||||
func updateMemberRoles(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
userId := props["user_id"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("updateMemberRoles", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
teamId := c.TeamId
|
||||
|
||||
newRoles := props["new_roles"]
|
||||
if !(model.IsValidUserRoles(newRoles)) {
|
||||
c.SetInvalidParam("updateMemberRoles", "new_roles")
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, teamId, model.PERMISSION_MANAGE_TEAM_ROLES) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM_ROLES)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.UpdateTeamMemberRoles(teamId, userId, newRoles); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
rdata := map[string]string{}
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if len(c.TeamId) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if team, err := c.App.GetTeam(c.TeamId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if c.HandleEtag(team.Etag(), "Get My Team", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, team.Etag())
|
||||
|
||||
c.App.SanitizeTeam(c.Session, team)
|
||||
|
||||
w.Write([]byte(team.ToJson()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamStats(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stats, err := c.App.GetTeamStats(c.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(stats.ToJson()))
|
||||
}
|
||||
|
||||
func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_IMPORT_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_IMPORT_TEAM)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(10000000); err != nil {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.parse.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
importFromArray, ok := r.MultipartForm.Value["importFrom"]
|
||||
if !ok || len(importFromArray) < 1 {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.no_import_from.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
importFrom := importFromArray[0]
|
||||
|
||||
fileSizeStr, ok := r.MultipartForm.Value["filesize"]
|
||||
if !ok || len(fileSizeStr) < 1 {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.unavailable.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fileSize, err := strconv.ParseInt(fileSizeStr[0], 10, 64)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.integer.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fileInfoArray, ok := r.MultipartForm.File["file"]
|
||||
if !ok {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(fileInfoArray) <= 0 {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.array.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fileInfo := fileInfoArray[0]
|
||||
|
||||
fileData, err := fileInfo.Open()
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("importTeam", "api.team.import_team.open.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer fileData.Close()
|
||||
|
||||
var log *bytes.Buffer
|
||||
switch importFrom {
|
||||
case "slack":
|
||||
var err *model.AppError
|
||||
if err, log = c.App.SlackImport(fileData, fileSize, c.TeamId); err != nil {
|
||||
c.Err = err
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=MattermostImportLog.txt")
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
if c.Err != nil {
|
||||
w.WriteHeader(c.Err.StatusCode)
|
||||
}
|
||||
io.Copy(w, bytes.NewReader(log.Bytes()))
|
||||
//http.ServeContent(w, r, "MattermostImportLog.txt", time.Now(), bytes.NewReader(log.Bytes()))
|
||||
}
|
||||
|
||||
func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
m := model.MapFromJson(r.Body)
|
||||
inviteId := m["invite_id"]
|
||||
|
||||
if team, err := c.App.GetTeamByInviteId(inviteId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
if !(team.Type == model.TEAM_OPEN) {
|
||||
c.Err = model.NewAppError("getInviteInfo", "api.team.get_invite_info.not_open_team", nil, "id="+inviteId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
result := map[string]string{}
|
||||
result["display_name"] = team.DisplayName
|
||||
result["description"] = team.Description
|
||||
result["name"] = team.Name
|
||||
result["id"] = team.Id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getTeamMembers", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(params["limit"])
|
||||
if err != nil {
|
||||
c.SetInvalidParam("getTeamMembers", "limit")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if members, err := c.App.GetTeamMembers(c.TeamId, offset, limit); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.TeamMembersToJson(members)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
userId := params["user_id"]
|
||||
if len(userId) < 26 {
|
||||
c.SetInvalidParam("getTeamMember", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if member, err := c.App.GetTeamMember(c.TeamId, userId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(member.ToJson()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
userIds := model.ArrayFromJson(r.Body)
|
||||
if len(userIds) == 0 {
|
||||
c.SetInvalidParam("getTeamMembersByIds", "user_ids")
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.GetTeamByTeamId(c.TeamId) == nil {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if members, err := c.App.GetTeamMembersByIds(c.TeamId, userIds); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.TeamMembersToJson(members)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func sanitizeTeamMap(c *Context, teams map[string]*model.Team) {
|
||||
for _, team := range teams {
|
||||
c.App.SanitizeTeam(c.Session, team)
|
||||
}
|
||||
}
|
||||
1196
api/team_test.go
1196
api/team_test.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1245
api/user.go
1245
api/user.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
2737
api/user_test.go
2737
api/user_test.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
328
api/webhook.go
328
api/webhook.go
@@ -1,328 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitWebhook() {
|
||||
api.BaseRoutes.Hooks.Handle("/incoming/create", api.ApiUserRequired(createIncomingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/incoming/update", api.ApiUserRequired(updateIncomingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/incoming/delete", api.ApiUserRequired(deleteIncomingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/incoming/list", api.ApiUserRequired(getIncomingHooks)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Hooks.Handle("/outgoing/create", api.ApiUserRequired(createOutgoingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/outgoing/update", api.ApiUserRequired(updateOutgoingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/outgoing/regen_token", api.ApiUserRequired(regenOutgoingHookToken)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/outgoing/delete", api.ApiUserRequired(deleteOutgoingHook)).Methods("POST")
|
||||
api.BaseRoutes.Hooks.Handle("/outgoing/list", api.ApiUserRequired(getOutgoingHooks)).Methods("GET")
|
||||
}
|
||||
|
||||
func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
hook := model.IncomingWebhookFromJson(r.Body)
|
||||
if hook == nil {
|
||||
c.SetInvalidParam("createIncomingHook", "webhook")
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := c.App.GetChannel(hook.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.LogAudit("fail - bad channel permissions")
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
if incomingHook, err := c.App.CreateIncomingWebhookForChannel(c.Session.UserId, channel, hook); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(incomingHook.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
hook := model.IncomingWebhookFromJson(r.Body)
|
||||
|
||||
if hook == nil {
|
||||
c.SetInvalidParam("updateIncomingHook", "webhook")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
oldHook, err := c.App.GetIncomingWebhook(hook.Id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.TeamId != oldHook.TeamId {
|
||||
c.Err = model.NewAppError("updateIncomingHook", "api.webhook.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, oldHook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != oldHook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, oldHook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := c.App.GetChannel(hook.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) {
|
||||
c.LogAudit("fail - bad channel permissions")
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
rhook, err := c.App.UpdateIncomingWebhook(oldHook, hook)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(rhook.ToJson()))
|
||||
}
|
||||
|
||||
func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteIncomingHook", "id")
|
||||
return
|
||||
}
|
||||
|
||||
hook, err := c.App.GetIncomingWebhook(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if c.Session.UserId != hook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeleteIncomingWebhook(id); err != nil {
|
||||
c.LogAudit("fail")
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
|
||||
func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if hooks, err := c.App.GetIncomingWebhooksForTeamPage(c.TeamId, 0, 100); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.IncomingWebhookListToJson(hooks)))
|
||||
}
|
||||
}
|
||||
|
||||
func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
hook := model.OutgoingWebhookFromJson(r.Body)
|
||||
if hook == nil {
|
||||
c.SetInvalidParam("createOutgoingHook", "webhook")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
hook.TeamId = c.TeamId
|
||||
hook.CreatorId = c.Session.UserId
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if rhook, err := c.App.CreateOutgoingWebhook(hook); err != nil {
|
||||
c.LogAudit("fail")
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(rhook.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if hooks, err := c.App.GetOutgoingWebhooksForTeamPage(c.TeamId, 0, 100); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.OutgoingWebhookListToJson(hooks)))
|
||||
}
|
||||
}
|
||||
|
||||
func updateOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.LogAudit("attempt")
|
||||
|
||||
hook := model.OutgoingWebhookFromJson(r.Body)
|
||||
|
||||
if hook == nil {
|
||||
c.SetInvalidParam("updateOutgoingHook", "webhook")
|
||||
return
|
||||
}
|
||||
|
||||
oldHook, err := c.App.GetOutgoingWebhook(hook.Id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.TeamId != oldHook.TeamId {
|
||||
c.Err = model.NewAppError("updateOutgoingHook", "api.webhook.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, oldHook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != oldHook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, oldHook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
rhook, err := c.App.UpdateOutgoingWebhook(oldHook, hook)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(rhook.ToJson()))
|
||||
}
|
||||
|
||||
func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteIncomingHook", "id")
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
hook, err := c.App.GetOutgoingWebhook(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != hook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeleteOutgoingWebhook(id); err != nil {
|
||||
c.LogAudit("fail")
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
|
||||
func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("regenOutgoingHookToken", "id")
|
||||
return
|
||||
}
|
||||
|
||||
hook, err := c.App.GetOutgoingWebhook(id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if c.TeamId != hook.TeamId {
|
||||
c.Err = model.NewAppError("regenOutgoingHookToken", "api.webhook.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != hook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
|
||||
return
|
||||
}
|
||||
|
||||
if rhook, err := c.App.RegenOutgoingWebhookToken(hook); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(rhook.ToJson()))
|
||||
}
|
||||
}
|
||||
@@ -1,968 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestCreateIncomingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
user := th.SystemAdminUser
|
||||
team := th.SystemAdminTeam
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
channel2 := th.CreatePrivateChannel(Client, team)
|
||||
channel3 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
|
||||
var rhook *model.IncomingWebhook
|
||||
if result, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rhook = result.Data.(*model.IncomingWebhook)
|
||||
}
|
||||
|
||||
if hook.ChannelId != rhook.ChannelId {
|
||||
t.Fatal("channel ids didn't match")
|
||||
}
|
||||
|
||||
if rhook.UserId != user.Id {
|
||||
t.Fatal("user ids didn't match")
|
||||
}
|
||||
|
||||
if rhook.TeamId != team.Id {
|
||||
t.Fatal("team ids didn't match")
|
||||
}
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: "junk"}
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel2.Id, UserId: "123", TeamId: "456"}
|
||||
if result, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.IncomingWebhook).UserId != user.Id {
|
||||
t.Fatal("bad user id wasn't overwritten")
|
||||
}
|
||||
if result.Data.(*model.IncomingWebhook).TeamId != team.Id {
|
||||
t.Fatal("bad team id wasn't overwritten")
|
||||
}
|
||||
}
|
||||
|
||||
Client.Must(Client.LeaveChannel(channel3.Id))
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel3.Id, UserId: user.Id, TeamId: team.Id}
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
th.UpdateUserToTeamAdmin(user2, team)
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel2.Id}
|
||||
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - channel is private and not a member")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = false })
|
||||
|
||||
if _, err := Client.CreateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateIncomingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
channel2 := th.CreatePrivateChannel(Client, team)
|
||||
channel3 := th.CreateChannel(Client, team)
|
||||
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
team2 := th.CreateTeam(Client)
|
||||
user3 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user3, team2)
|
||||
th.UpdateUserToTeamAdmin(user3, team2)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
hook := createIncomingWebhook(channel1.Id, Client, t)
|
||||
|
||||
t.Run("UpdateIncomingHook", func(t *testing.T) {
|
||||
hook.DisplayName = "hook2"
|
||||
hook.Description = "description"
|
||||
hook.ChannelId = channel3.Id
|
||||
|
||||
if result, err := Client.UpdateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.IncomingWebhook)
|
||||
|
||||
if updatedHook.DisplayName != "hook2" {
|
||||
t.Fatal("Hook name is not updated")
|
||||
}
|
||||
|
||||
if updatedHook.Description != "description" {
|
||||
t.Fatal("Hook description is not updated")
|
||||
}
|
||||
|
||||
if updatedHook.ChannelId != channel3.Id {
|
||||
t.Fatal("Hook channel is not updated")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RetainCreateAt", func(t *testing.T) {
|
||||
hook2 := &model.IncomingWebhook{ChannelId: channel1.Id, CreateAt: 100}
|
||||
|
||||
if result, err := Client.CreateIncomingWebhook(hook2); err != nil {
|
||||
t.Fatal("hook creation failed")
|
||||
} else {
|
||||
createdHook := result.Data.(*model.IncomingWebhook)
|
||||
createdHook.DisplayName = "Name2"
|
||||
|
||||
if result, err := Client.UpdateIncomingWebhook(createdHook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.IncomingWebhook)
|
||||
|
||||
if updatedHook.CreateAt != createdHook.CreateAt {
|
||||
t.Fatal("failed - hook create at should not be changed")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ModifyUpdateAt", func(t *testing.T) {
|
||||
hook.DisplayName = "Name3"
|
||||
|
||||
if result, err := Client.UpdateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.IncomingWebhook)
|
||||
|
||||
if updatedHook.UpdateAt == hook.UpdateAt {
|
||||
t.Fatal("failed - hook updateAt is not updated")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateNonExistentHook", func(t *testing.T) {
|
||||
nonExistentHook := &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
|
||||
if _, err := Client.UpdateIncomingWebhook(nonExistentHook); err == nil {
|
||||
t.Fatal("should have failed - update a non-existent hook")
|
||||
}
|
||||
})
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
t.Run("UserIsNotAdminOfTeam", func(t *testing.T) {
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - user is not admin of team")
|
||||
}
|
||||
})
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
t.Run("OnlyAdminIntegrationsDisabled", func(t *testing.T) {
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
t.Run("UpdateHookOfSameUser", func(t *testing.T) {
|
||||
sameUserHook := &model.IncomingWebhook{ChannelId: channel1.Id, UserId: user2.Id}
|
||||
if result, err := Client.CreateIncomingWebhook(sameUserHook); err != nil {
|
||||
t.Fatal("Hook creation failed")
|
||||
} else {
|
||||
sameUserHook = result.Data.(*model.IncomingWebhook)
|
||||
}
|
||||
|
||||
if _, err := Client.UpdateIncomingWebhook(sameUserHook); err != nil {
|
||||
t.Fatal("should not fail - only admin integrations are disabled & hook of same user")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateHookOfDifferentUser", func(t *testing.T) {
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - user does not have permissions to update other user's hooks")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
Client.Logout()
|
||||
th.UpdateUserToTeamAdmin(user2, team)
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
t.Run("UpdateByDifferentUser", func(t *testing.T) {
|
||||
if result, err := Client.UpdateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.IncomingWebhook)
|
||||
|
||||
if updatedHook.UserId == user2.Id {
|
||||
t.Fatal("Hook's creator userId is not retained")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IncomingHooksDisabled", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = false })
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - incoming hooks are disabled")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PrivateChannel", func(t *testing.T) {
|
||||
hook.ChannelId = channel2.Id
|
||||
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - updating to a private channel where the user is not a member")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateToNonExistentChannel", func(t *testing.T) {
|
||||
hook.ChannelId = "junk"
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
})
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user3.Id, user3.Password))
|
||||
Client.SetTeamId(team2.Id)
|
||||
t.Run("UpdateToADifferentTeam", func(t *testing.T) {
|
||||
if _, err := Client.UpdateIncomingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - update to a different team is not allowed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func createIncomingWebhook(channelID string, Client *model.Client, t *testing.T) *model.IncomingWebhook {
|
||||
hook := &model.IncomingWebhook{ChannelId: channelID}
|
||||
if result, err := Client.CreateIncomingWebhook(hook); err != nil {
|
||||
t.Fatal("Hook creation failed")
|
||||
} else {
|
||||
hook = result.Data.(*model.IncomingWebhook)
|
||||
}
|
||||
|
||||
return hook
|
||||
}
|
||||
|
||||
func createOutgoingWebhook(channelID string, callbackURLs []string, triggerWords []string, Client *model.Client, t *testing.T) *model.OutgoingWebhook {
|
||||
hook := &model.OutgoingWebhook{ChannelId: channelID, CallbackURLs: callbackURLs, TriggerWords: triggerWords}
|
||||
if result, err := Client.CreateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("Hook creation failed")
|
||||
} else {
|
||||
hook = result.Data.(*model.OutgoingWebhook)
|
||||
}
|
||||
|
||||
return hook
|
||||
}
|
||||
|
||||
func TestListIncomingHooks(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook1 := &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook)
|
||||
|
||||
hook2 := &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
hook2 = Client.Must(Client.CreateIncomingWebhook(hook2)).Data.(*model.IncomingWebhook)
|
||||
|
||||
if result, err := Client.ListIncomingWebhooks(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
hooks := result.Data.([]*model.IncomingWebhook)
|
||||
|
||||
if len(hooks) != 2 {
|
||||
t.Fatal("incorrect number of hooks")
|
||||
}
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.ListIncomingWebhooks(); err == nil {
|
||||
t.Fatal("should have errored - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.ListIncomingWebhooks(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = false })
|
||||
|
||||
if _, err := Client.ListIncomingWebhooks(); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteIncomingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook)
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook("junk"); err == nil {
|
||||
t.Fatal("should have failed - bad id")
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(""); err == nil {
|
||||
t.Fatal("should have failed - empty id")
|
||||
}
|
||||
|
||||
hooks := Client.Must(Client.ListIncomingWebhooks()).Data.([]*model.IncomingWebhook)
|
||||
if len(hooks) != 0 {
|
||||
t.Fatal("delete didn't work properly")
|
||||
}
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook)
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - not creator or team admin")
|
||||
}
|
||||
|
||||
hook = &model.IncomingWebhook{ChannelId: channel1.Id}
|
||||
hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook)
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = false })
|
||||
|
||||
if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOutgoingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
user := th.SystemAdminUser
|
||||
team := th.SystemAdminTeam
|
||||
team2 := th.CreateTeam(Client)
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
channel2 := th.CreatePrivateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
user3 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user3, team2)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
|
||||
var rhook *model.OutgoingWebhook
|
||||
if result, err := Client.CreateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rhook = result.Data.(*model.OutgoingWebhook)
|
||||
}
|
||||
|
||||
if hook.ChannelId != rhook.ChannelId {
|
||||
t.Fatal("channel ids didn't match")
|
||||
}
|
||||
|
||||
if rhook.CreatorId != user.Id {
|
||||
t.Fatal("user ids didn't match")
|
||||
}
|
||||
|
||||
if rhook.TeamId != team.Id {
|
||||
t.Fatal("team ids didn't match")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, TriggerWords: []string{"cats", "dogs"}, CallbackURLs: []string{"http://nowhere.com", "http://cats.com"}}
|
||||
hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, TriggerWords: []string{"cats"}, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("multiple trigger words and urls failed")
|
||||
}
|
||||
|
||||
if _, err := Client.CreateOutgoingWebhook(hook1); err == nil {
|
||||
t.Fatal("should have failed - duplicate trigger words and urls")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: "junk", CallbackURLs: []string{"http://nowhere.com"}}
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - bad channel id")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CreatorId: "123", TeamId: "456", CallbackURLs: []string{"http://nowhere.com"}}
|
||||
if result, err := Client.CreateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.OutgoingWebhook).CreatorId != user.Id {
|
||||
t.Fatal("bad user id wasn't overwritten")
|
||||
}
|
||||
if result.Data.(*model.OutgoingWebhook).TeamId != team.Id {
|
||||
t.Fatal("bad team id wasn't overwritten")
|
||||
}
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel2.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - private channel")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{CallbackURLs: []string{"http://nowhere.com"}}
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - blank channel and trigger words")
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user3.Id, user3.Password))
|
||||
Client.SetTeamId(team2.Id)
|
||||
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - wrong team")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = false })
|
||||
|
||||
if _, err := Client.CreateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOutgoingHooks(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
hook2 := &model.OutgoingWebhook{TriggerWords: []string{"trigger"}, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook2 = Client.Must(Client.CreateOutgoingWebhook(hook2)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if result, err := Client.ListOutgoingWebhooks(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
hooks := result.Data.([]*model.OutgoingWebhook)
|
||||
|
||||
if len(hooks) != 2 {
|
||||
t.Fatal("incorrect number of hooks")
|
||||
}
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.ListOutgoingWebhooks(); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.ListOutgoingWebhooks(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = false })
|
||||
|
||||
if _, err := Client.ListOutgoingWebhooks(); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOutgoingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
user := th.SystemAdminUser
|
||||
team := th.SystemAdminTeam
|
||||
team2 := th.CreateTeam(Client)
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
channel2 := th.CreatePrivateChannel(Client, team)
|
||||
channel3 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
user3 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user3, team2)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"cats"}, Client, t)
|
||||
createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"dogs"}, Client, t)
|
||||
|
||||
hook.DisplayName = "Cats"
|
||||
hook.Description = "Get me some cats"
|
||||
t.Run("OutgoingHooksDisabled", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = false })
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - outgoing webhooks disabled")
|
||||
}
|
||||
})
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
t.Run("UpdateOutgoingWebhook", func(t *testing.T) {
|
||||
if result, err := Client.UpdateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("failed to update outgoing web hook")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if updatedHook.DisplayName != hook.DisplayName {
|
||||
t.Fatal("Hook display name did not get updated")
|
||||
}
|
||||
|
||||
if updatedHook.Description != hook.Description {
|
||||
t.Fatal("Hook description did not get updated")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RetainCreateAt", func(t *testing.T) {
|
||||
hook2 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}, TriggerWords: []string{"rats"}}
|
||||
|
||||
if result, err := Client.CreateOutgoingWebhook(hook2); err != nil {
|
||||
t.Fatal("hook creation failed")
|
||||
} else {
|
||||
createdHook := result.Data.(*model.OutgoingWebhook)
|
||||
createdHook.DisplayName = "Name2"
|
||||
|
||||
if result, err := Client.UpdateOutgoingWebhook(createdHook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if updatedHook.CreateAt != createdHook.CreateAt {
|
||||
t.Fatal("failed - hook create at should not be changed")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ModifyUpdateAt", func(t *testing.T) {
|
||||
hook.DisplayName = "Name3"
|
||||
|
||||
if result, err := Client.UpdateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("Update hook should not fail")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if updatedHook.UpdateAt == hook.UpdateAt {
|
||||
t.Fatal("failed - hook updateAt is not updated")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - user does not have permissions to manage webhooks")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook2 := createOutgoingWebhook(channel1.Id, []string{"http://nowhereelse.com"}, []string{"dogs"}, Client, t)
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook2); err != nil {
|
||||
t.Fatal("update webhook failed when admin only integrations is turned off")
|
||||
}
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
Client.Logout()
|
||||
th.LinkUserToTeam(user3, team)
|
||||
th.UpdateUserToTeamAdmin(user3, team)
|
||||
Client.Must(Client.LoginById(user3.Id, user3.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
t.Run("RetainHookCreator", func(t *testing.T) {
|
||||
if result, err := Client.UpdateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("failed to update outgoing web hook")
|
||||
} else {
|
||||
updatedHook := result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if updatedHook.CreatorId != user.Id {
|
||||
t.Fatal("hook creator should not be changed")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user.Id, user.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
t.Run("UpdateToExistingTriggerWordAndCallback", func(t *testing.T) {
|
||||
t.Run("OnSameChannel", func(t *testing.T) {
|
||||
hook.TriggerWords = []string{"dogs"}
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - duplicate trigger words & channel urls")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OnDifferentChannel", func(t *testing.T) {
|
||||
hook.TriggerWords = []string{"dogs"}
|
||||
hook.ChannelId = channel3.Id
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err != nil {
|
||||
t.Fatal("update of hook failed with duplicate trigger word but different channel")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("UpdateToNonExistentChannel", func(t *testing.T) {
|
||||
hook.ChannelId = "junk"
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - non existent channel")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateToPrivateChannel", func(t *testing.T) {
|
||||
hook.ChannelId = channel2.Id
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - update to a private channel")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateToBlankTriggerWordAndChannel", func(t *testing.T) {
|
||||
hook.ChannelId = ""
|
||||
hook.TriggerWords = nil
|
||||
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - update to blank trigger words & channel")
|
||||
}
|
||||
})
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user3.Id, user3.Password))
|
||||
Client.SetTeamId(team2.Id)
|
||||
t.Run("UpdateToADifferentTeam", func(t *testing.T) {
|
||||
if _, err := Client.UpdateOutgoingWebhook(hook); err == nil {
|
||||
t.Fatal("should have failed - update to a different team is not allowed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteOutgoingHook(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook("junk"); err == nil {
|
||||
t.Fatal("should have failed - bad hook id")
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(""); err == nil {
|
||||
t.Fatal("should have failed - empty hook id")
|
||||
}
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hooks := Client.Must(Client.ListOutgoingWebhooks()).Data.([]*model.OutgoingWebhook)
|
||||
if len(hooks) != 0 {
|
||||
t.Fatal("delete didn't work properly")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - not creator or team admin")
|
||||
}
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = false })
|
||||
|
||||
if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegenOutgoingHookToken(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.SystemAdminClient
|
||||
team := th.SystemAdminTeam
|
||||
team2 := th.CreateTeam(Client)
|
||||
channel1 := th.CreateChannel(Client, team)
|
||||
user2 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user2, team)
|
||||
user3 := th.CreateUser(Client)
|
||||
th.LinkUserToTeam(user3, team2)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID)
|
||||
|
||||
// Revoke permission from regular users.
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken("junk"); err == nil {
|
||||
t.Fatal("should have failed - bad id")
|
||||
}
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken(""); err == nil {
|
||||
t.Fatal("should have failed - empty id")
|
||||
}
|
||||
|
||||
if result, err := Client.RegenOutgoingWebhookToken(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.OutgoingWebhook).Token == hook.Token {
|
||||
t.Fatal("regen didn't work properly")
|
||||
}
|
||||
}
|
||||
|
||||
Client.SetTeamId(model.NewId())
|
||||
if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - wrong team id")
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user2.Id, user2.Password))
|
||||
Client.SetTeamId(team.Id)
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - not system/team admin")
|
||||
}
|
||||
|
||||
// Grant permission to regular users.
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}}
|
||||
hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Client.Logout()
|
||||
Client.Must(Client.LoginById(user3.Id, user3.Password))
|
||||
Client.SetTeamId(team2.Id)
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil {
|
||||
t.Fatal("should have failed - wrong team")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = false })
|
||||
|
||||
if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil {
|
||||
t.Fatal("should have errored - webhooks turned off")
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (api *API) InitWebrtc() {
|
||||
api.BaseRoutes.Webrtc.Handle("/token", api.ApiUserRequired(webrtcToken)).Methods("POST")
|
||||
}
|
||||
|
||||
func webrtcToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
result, err := c.App.GetWebrtcInfoForSession(c.Session.Id)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(result.ToJson()))
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitWebSocket() {
|
||||
api.BaseRoutes.Users.Handle("/websocket", api.ApiAppHandlerTrustRequester(connect)).Methods("GET")
|
||||
}
|
||||
|
||||
func connect(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
upgrader := websocket.Upgrader{
|
||||
ReadBufferSize: model.SOCKET_MAX_MESSAGE_SIZE_KB,
|
||||
WriteBufferSize: model.SOCKET_MAX_MESSAGE_SIZE_KB,
|
||||
CheckOrigin: c.App.OriginChecker(),
|
||||
}
|
||||
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprintf("websocket connect err: %v", err))
|
||||
c.Err = model.NewAppError("connect", "api.web_socket.connect.upgrade.app_error", nil, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
wc := c.App.NewWebConn(ws, c.Session, c.T, c.Locale)
|
||||
|
||||
if len(c.Session.UserId) > 0 {
|
||||
c.App.HubRegister(wc)
|
||||
}
|
||||
|
||||
wc.Pump()
|
||||
}
|
||||
@@ -1,381 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//"encoding/json"
|
||||
//"net/http"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
/*func TestWebSocketAuthentication(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
WebSocketClient.SendMessage("ping", nil)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Data["text"].(string) != "pong" {
|
||||
t.Fatal("wrong response")
|
||||
}
|
||||
|
||||
WebSocketClient.Close()
|
||||
|
||||
authToken := WebSocketClient.AuthToken
|
||||
WebSocketClient.AuthToken = "junk"
|
||||
if err := WebSocketClient.Connect(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
WebSocketClient.Listen()
|
||||
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp != nil {
|
||||
t.Fatal("should have closed")
|
||||
}
|
||||
|
||||
if conn, _, err := websocket.DefaultDialer.Dial(WebSocketClient.ApiUrl+"/users/websocket", nil); err != nil {
|
||||
t.Fatal("should have connected")
|
||||
} else {
|
||||
req := &model.WebSocketRequest{}
|
||||
req.Seq = 1
|
||||
req.Action = "ping"
|
||||
conn.WriteJSON(req)
|
||||
|
||||
closedAutomatically := false
|
||||
hitNotAuthedError := false
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Second)
|
||||
conn.Close()
|
||||
|
||||
if !closedAutomatically {
|
||||
t.Fatal("should have closed automatically in 5 seconds")
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
if _, rawMsg, err := conn.ReadMessage(); err != nil {
|
||||
closedAutomatically = true
|
||||
conn.Close()
|
||||
break
|
||||
} else {
|
||||
var response model.WebSocketResponse
|
||||
if err := json.Unmarshal(rawMsg, &response); err != nil && !response.IsValid() {
|
||||
t.Fatal("should not have failed")
|
||||
} else {
|
||||
if response.Error == nil || response.Error.Id != "api.web_socket_router.not_authenticated.app_error" {
|
||||
t.Log(response.Error.Id)
|
||||
t.Fatal("wrong error")
|
||||
continue
|
||||
}
|
||||
|
||||
hitNotAuthedError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hitNotAuthedError {
|
||||
t.Fatal("should have received a not authenticated response")
|
||||
}
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
header.Set(model.HEADER_AUTH, "BEARER "+authToken)
|
||||
if conn, _, err := websocket.DefaultDialer.Dial(WebSocketClient.ApiUrl+"/users/websocket", header); err != nil {
|
||||
t.Fatal("should have connected")
|
||||
} else {
|
||||
if _, rawMsg, err := conn.ReadMessage(); err != nil {
|
||||
t.Fatal("should not have closed automatically")
|
||||
} else {
|
||||
var event model.WebSocketEvent
|
||||
if err := json.Unmarshal(rawMsg, &event); err != nil && !event.IsValid() {
|
||||
t.Fatal("should not have failed")
|
||||
} else if event.Event != model.WEBSOCKET_EVENT_HELLO {
|
||||
t.Log(event.ToJson())
|
||||
t.Fatal("should have helloed")
|
||||
}
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
}*/
|
||||
|
||||
func TestWebSocket(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Test closing and reconnecting
|
||||
WebSocketClient.Close()
|
||||
if err := WebSocketClient.Connect(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
WebSocketClient.SendMessage("ping", nil)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Data["text"].(string) != "pong" {
|
||||
t.Fatal("wrong response")
|
||||
}
|
||||
|
||||
WebSocketClient.SendMessage("", nil)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.no_action.app_error" {
|
||||
t.Fatal("should have been no action response")
|
||||
}
|
||||
|
||||
WebSocketClient.SendMessage("junk", nil)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.bad_action.app_error" {
|
||||
t.Fatal("should have been bad action response")
|
||||
}
|
||||
|
||||
req := &model.WebSocketRequest{}
|
||||
req.Seq = 0
|
||||
req.Action = "ping"
|
||||
WebSocketClient.Conn.WriteJSON(req)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.bad_seq.app_error" {
|
||||
t.Fatal("should have been bad action response")
|
||||
}
|
||||
|
||||
WebSocketClient.UserTyping("", "")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.websocket_handler.invalid_param.app_error" {
|
||||
t.Fatal("should have been invalid param response")
|
||||
} else {
|
||||
if resp.Error.DetailedError != "" {
|
||||
t.Fatal("detailed error not cleared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebSocketEvent(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
omitUser := make(map[string]bool, 1)
|
||||
omitUser["somerandomid"] = true
|
||||
evt1 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", th.BasicChannel.Id, "", omitUser)
|
||||
evt1.Add("user_id", "somerandomid")
|
||||
th.App.Publish(evt1)
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
stop := make(chan bool)
|
||||
eventHit := false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_TYPING && resp.Data["user_id"].(string) == "somerandomid" {
|
||||
eventHit = true
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if !eventHit {
|
||||
t.Fatal("did not receive typing event")
|
||||
}
|
||||
|
||||
evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil)
|
||||
th.App.Publish(evt2)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
eventHit = false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_TYPING {
|
||||
eventHit = true
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if eventHit {
|
||||
t.Fatal("got typing event for bad channel id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDirectChannelWithSocket(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.BasicClient
|
||||
user2 := th.BasicUser2
|
||||
|
||||
users := make([]*model.User, 0)
|
||||
users = append(users, user2)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
users = append(users, th.CreateUser(Client))
|
||||
}
|
||||
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
wsr := <-WebSocketClient.EventChannel
|
||||
if wsr.Event != model.WEBSOCKET_EVENT_HELLO {
|
||||
t.Fatal("missing hello")
|
||||
}
|
||||
|
||||
stop := make(chan bool)
|
||||
count := 0
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case wsr := <-WebSocketClient.EventChannel:
|
||||
if wsr.Event == model.WEBSOCKET_EVENT_DIRECT_ADDED {
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, user := range users {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, err := Client.CreateDirectChannel(user.Id); err != nil {
|
||||
t.Fatal("failed to create DM channel")
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(5000 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if count != len(users) {
|
||||
t.Fatal("We didn't get the proper amount of direct_added messages")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWebsocketOriginSecurity(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
url := fmt.Sprintf("ws://localhost:%v", th.App.Srv.ListenAddr.Port)
|
||||
|
||||
// Should fail because origin doesn't match
|
||||
_, _, err := websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!")
|
||||
}
|
||||
|
||||
// We are not a browser so we can spoof this just fine
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port)},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should succeed now because open CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "*" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should succeed now because matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.evil.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should fail because non-matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin contain AllowCorsFrom")
|
||||
}
|
||||
|
||||
// Should fail because non-matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
|
||||
"Origin": []string{"http://www.good.co"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "" })
|
||||
}
|
||||
@@ -113,7 +113,7 @@ type API struct {
|
||||
BaseRoutes *Routes
|
||||
}
|
||||
|
||||
func Init(a *app.App, root *mux.Router, full bool) *API {
|
||||
func Init(a *app.App, root *mux.Router) *API {
|
||||
api := &API{
|
||||
App: a,
|
||||
BaseRoutes: &Routes{},
|
||||
@@ -231,10 +231,7 @@ func Init(a *app.App, root *mux.Router, full bool) *API {
|
||||
|
||||
root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))
|
||||
|
||||
// REMOVE CONDITION WHEN APIv3 REMOVED
|
||||
if full {
|
||||
a.InitEmailBatching()
|
||||
}
|
||||
a.InitEmailBatching()
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||
Init(th.App, th.App.Srv.Router, true)
|
||||
Init(th.App, th.App.Srv.Router)
|
||||
web.NewWeb(th.App, th.App.Srv.Router)
|
||||
wsapi.Init(th.App, th.App.Srv.WebSocketRouter)
|
||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||
|
||||
449
api4/commands_test.go
Обычный файл
449
api4/commands_test.go
Обычный файл
@@ -0,0 +1,449 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestEchoCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel1 := th.BasicChannel
|
||||
|
||||
echoTestString := "/echo test"
|
||||
|
||||
if r1 := Client.Must(Client.ExecuteCommand(channel1.Id, echoTestString)).(*model.CommandResponse); r1 == nil {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
if r1 := Client.Must(Client.ExecuteCommand(channel1.Id, "/echo ")).(*model.CommandResponse); r1 == nil {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPostsForChannel(channel1.Id, 0, 2, "")).(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Echo command failed to send")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupmsgCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
team := th.BasicTeam
|
||||
user1 := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
user3 := th.CreateUser()
|
||||
user4 := th.CreateUser()
|
||||
user5 := th.CreateUser()
|
||||
user6 := th.CreateUser()
|
||||
user7 := th.CreateUser()
|
||||
user8 := th.CreateUser()
|
||||
user9 := th.CreateUser()
|
||||
th.LinkUserToTeam(user3, team)
|
||||
th.LinkUserToTeam(user4, team)
|
||||
|
||||
rs1 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg "+user2.Username+","+user3.Username)).(*model.CommandResponse)
|
||||
|
||||
group1 := model.GetGroupNameFromUserIds([]string{user1.Id, user2.Id, user3.Id})
|
||||
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+group1) {
|
||||
t.Fatal("failed to create group channel")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg "+user3.Username+","+user4.Username+" foobar")).(*model.CommandResponse)
|
||||
group2 := model.GetGroupNameFromUserIds([]string{user1.Id, user3.Id, user4.Id})
|
||||
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+group2) {
|
||||
t.Fatal("failed to create second direct channel")
|
||||
}
|
||||
if result := Client.Must(Client.SearchPosts(team.Id, "foobar", false)).(*model.PostList); len(result.Order) == 0 {
|
||||
t.Fatal("post did not get sent to direct message")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg "+user2.Username+","+user3.Username)).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+group1) {
|
||||
t.Fatal("failed to go back to existing group channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg "+user2.Username+" foobar"))
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg "+user2.Username+","+user3.Username+","+user4.Username+","+user5.Username+","+user6.Username+","+user7.Username+","+user8.Username+","+user9.Username+" foobar"))
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg junk foobar"))
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/groupmsg junk,junk2 foobar"))
|
||||
}
|
||||
|
||||
func TestInvitePeopleCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
r1 := Client.Must(Client.ExecuteCommand(channel.Id, "/invite_people test@example.com")).(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
r2 := Client.Must(Client.ExecuteCommand(channel.Id, "/invite_people test1@example.com test2@example.com")).(*model.CommandResponse)
|
||||
if r2 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
r3 := Client.Must(Client.ExecuteCommand(channel.Id, "/invite_people")).(*model.CommandResponse)
|
||||
if r3 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
}
|
||||
|
||||
// also used to test /open (see command_open_test.go)
|
||||
func testJoinCommands(t *testing.T, alias string) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
team := th.BasicTeam
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel0 := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel0 = Client.Must(Client.CreateChannel(channel0)).(*model.Channel)
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).(*model.Channel)
|
||||
Client.Must(Client.RemoveUserFromChannel(channel1.Id, th.BasicUser.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).(*model.Channel)
|
||||
Client.Must(Client.RemoveUserFromChannel(channel2.Id, th.BasicUser.Id))
|
||||
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(th.BasicUser.Id, user2.Id)).(*model.Channel)
|
||||
|
||||
rs5 := Client.Must(Client.ExecuteCommand(channel0.Id, "/"+alias+" "+channel2.Name)).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
|
||||
t.Fatal("failed to join channel")
|
||||
}
|
||||
|
||||
rs6 := Client.Must(Client.ExecuteCommand(channel0.Id, "/"+alias+" "+channel3.Name)).(*model.CommandResponse)
|
||||
if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
|
||||
t.Fatal("should not have joined direct message channel")
|
||||
}
|
||||
|
||||
c1 := Client.Must(Client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, "")).([]*model.Channel)
|
||||
|
||||
found := false
|
||||
for _, c := range c1 {
|
||||
if c.Id == channel2.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("did not join channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinCommands(t *testing.T) {
|
||||
testJoinCommands(t, "join")
|
||||
}
|
||||
|
||||
func TestLoadTestHelpCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.ExecuteCommand(channel.Id, "/test help")).(*model.CommandResponse)
|
||||
if !strings.Contains(rs.Text, "Mattermost testing commands to help") {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestSetupCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.ExecuteCommand(channel.Id, "/test setup fuzz 1 1 1")).(*model.CommandResponse)
|
||||
if rs.Text != "Created environment" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestUsersCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.ExecuteCommand(channel.Id, "/test users fuzz 1 2")).(*model.CommandResponse)
|
||||
if rs.Text != "Added users" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestChannelsCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.ExecuteCommand(channel.Id, "/test channels fuzz 1 2")).(*model.CommandResponse)
|
||||
if rs.Text != "Added channels" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestPostsCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableTesting = true })
|
||||
|
||||
rs := Client.Must(Client.ExecuteCommand(channel.Id, "/test posts fuzz 2 3 2")).(*model.CommandResponse)
|
||||
if rs.Text != "Added posts" {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLeaveCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
team := th.BasicTeam
|
||||
user2 := th.BasicUser2
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).(*model.Channel)
|
||||
Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).(*model.Channel)
|
||||
Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id))
|
||||
Client.Must(Client.AddChannelMember(channel2.Id, user2.Id))
|
||||
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(th.BasicUser.Id, user2.Id)).(*model.Channel)
|
||||
|
||||
rs1 := Client.Must(Client.ExecuteCommand(channel1.Id, "/leave")).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+model.DEFAULT_CHANNEL) {
|
||||
t.Fatal("failed to leave open channel 1")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.ExecuteCommand(channel2.Id, "/leave")).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+model.DEFAULT_CHANNEL) {
|
||||
t.Fatal("failed to leave private channel 1")
|
||||
}
|
||||
|
||||
_, err := Client.ExecuteCommand(channel3.Id, "/leave")
|
||||
if err == nil {
|
||||
t.Fatal("should fail leaving direct channel")
|
||||
}
|
||||
|
||||
cdata := Client.Must(Client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, "")).([]*model.Channel)
|
||||
|
||||
found := false
|
||||
for _, c := range cdata {
|
||||
if c.Id == channel1.Id || c.Id == channel2.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("did not leave right channels")
|
||||
}
|
||||
|
||||
for _, c := range cdata {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
if _, err := Client.RemoveUserFromChannel(c.Id, th.BasicUser.Id); err == nil {
|
||||
t.Fatal("should have errored on leaving default channel")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogoutTestCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Client.Must(th.Client.ExecuteCommand(th.BasicChannel.Id, "/logout"))
|
||||
}
|
||||
|
||||
func TestMeCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
testString := "/me hello"
|
||||
|
||||
r1 := Client.Must(Client.ExecuteCommand(channel.Id, testString)).(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "")).(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `*hello*` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
team := th.BasicTeam
|
||||
user1 := th.BasicUser
|
||||
user2 := th.BasicUser2
|
||||
user3 := th.CreateUser()
|
||||
th.LinkUserToTeam(user3, team)
|
||||
|
||||
Client.Must(Client.CreateDirectChannel(th.BasicUser.Id, user2.Id))
|
||||
Client.Must(Client.CreateDirectChannel(th.BasicUser.Id, user3.Id))
|
||||
|
||||
rs1 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/msg "+user2.Username)).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to create direct channel")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/msg "+user3.Username+" foobar")).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user3.Id) && !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user3.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to create second direct channel")
|
||||
}
|
||||
if result := Client.Must(Client.SearchPosts(th.BasicTeam.Id, "foobar", false)).(*model.PostList); len(result.Order) == 0 {
|
||||
t.Fatalf("post did not get sent to direct message")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/msg "+user2.Username)).(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) {
|
||||
t.Fatal("failed to go back to existing direct channel")
|
||||
}
|
||||
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/msg "+th.BasicUser.Username+" foobar"))
|
||||
Client.Must(Client.ExecuteCommand(th.BasicChannel.Id, "/msg junk foobar"))
|
||||
}
|
||||
|
||||
func TestOpenCommands(t *testing.T) {
|
||||
testJoinCommands(t, "open")
|
||||
}
|
||||
|
||||
func TestSearchCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Client.Must(th.Client.ExecuteCommand(th.BasicChannel.Id, "/search"))
|
||||
}
|
||||
|
||||
func TestSettingsCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Client.Must(th.Client.ExecuteCommand(th.BasicChannel.Id, "/settings"))
|
||||
}
|
||||
|
||||
func TestShortcutsCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Client.Must(th.Client.ExecuteCommand(th.BasicChannel.Id, "/shortcuts"))
|
||||
}
|
||||
|
||||
func TestShrugCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
|
||||
testString := "/shrug"
|
||||
|
||||
r1 := Client.Must(Client.ExecuteCommand(channel.Id, testString)).(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "")).(*model.PostList)
|
||||
if len(p1.Order) != 2 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `¯\\\_(ツ)\_/¯` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
commandAndTest(t, th, "away")
|
||||
commandAndTest(t, th, "offline")
|
||||
commandAndTest(t, th, "online")
|
||||
}
|
||||
|
||||
func commandAndTest(t *testing.T, th *TestHelper, status string) {
|
||||
Client := th.Client
|
||||
channel := th.BasicChannel
|
||||
user := th.BasicUser
|
||||
|
||||
r1 := Client.Must(Client.ExecuteCommand(channel.Id, "/"+status)).(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
rstatus := Client.Must(Client.GetUserStatus(user.Id, "")).(*model.Status)
|
||||
|
||||
if rstatus.Status != status {
|
||||
t.Fatal("Error setting status " + status)
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,19 @@
|
||||
package api4
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestCreateOAuthApp(t *testing.T) {
|
||||
@@ -726,3 +733,409 @@ func TestDeauthorizeOAuthApp(t *testing.T) {
|
||||
_, resp = Client.DeauthorizeOAuthApp(rapp.Id)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
}
|
||||
|
||||
func TestOAuthAccessToken(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
|
||||
enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||
oauthApp = Client.Must(Client.CreateOAuthApp(oauthApp)).(*model.OAuthApp)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false })
|
||||
data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{"12345678901234567890123456"}, "client_secret": []string{"12345678901234567890123456"}, "code": []string{"junk"}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
|
||||
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Log(resp.StatusCode)
|
||||
t.Fatal("should have failed - oauth providing turned off")
|
||||
}
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
authRequest := &model.AuthorizeRequest{
|
||||
ResponseType: model.AUTHCODE_RESPONSE_TYPE,
|
||||
ClientId: oauthApp.Id,
|
||||
RedirectUri: oauthApp.CallbackUrls[0],
|
||||
Scope: "all",
|
||||
State: "123",
|
||||
}
|
||||
|
||||
redirect, resp := Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ := url.Parse(redirect)
|
||||
|
||||
Client.Logout()
|
||||
|
||||
data = url.Values{"grant_type": []string{"junk"}, "client_id": []string{oauthApp.Id}, "client_secret": []string{oauthApp.ClientSecret}, "code": []string{rurl.Query().Get("code")}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
|
||||
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - bad grant type")
|
||||
}
|
||||
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", "")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - missing client id")
|
||||
}
|
||||
data.Set("client_id", "junk")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - bad client id")
|
||||
}
|
||||
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", "")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - missing client secret")
|
||||
}
|
||||
|
||||
data.Set("client_secret", "junk")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - bad client secret")
|
||||
}
|
||||
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("code", "")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - missing code")
|
||||
}
|
||||
|
||||
data.Set("code", "junk")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - bad code")
|
||||
}
|
||||
|
||||
data.Set("code", rurl.Query().Get("code"))
|
||||
data.Set("redirect_uri", "junk")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - non-matching redirect uri")
|
||||
}
|
||||
|
||||
// reset data for successful request
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("code", rurl.Query().Get("code"))
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
|
||||
token := ""
|
||||
refreshToken := ""
|
||||
if rsp, resp := Client.GetOAuthAccessToken(data); resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else {
|
||||
token = rsp.AccessToken
|
||||
refreshToken = rsp.RefreshToken
|
||||
}
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.DoApiGet("/users?page=0&per_page=100&access_token="+token, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, resp := Client.GetUsers(0, 100, ""); resp.Error == nil {
|
||||
t.Fatal("should have failed - no access token provided")
|
||||
}
|
||||
|
||||
if _, resp := Client.GetUsers(0, 100, ""); resp.Error == nil {
|
||||
t.Fatal("should have failed - bad access token provided")
|
||||
}
|
||||
|
||||
Client.SetOAuthToken(token)
|
||||
if users, resp := Client.GetUsers(0, 100, ""); resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if len(users) == 0 {
|
||||
t.Fatal("users empty - did not get results correctly")
|
||||
}
|
||||
}
|
||||
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("should have failed - tried to reuse auth code")
|
||||
}
|
||||
|
||||
data.Set("grant_type", model.REFRESH_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("refresh_token", "")
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
data.Del("code")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("Should have failed - refresh token empty")
|
||||
}
|
||||
|
||||
data.Set("refresh_token", refreshToken)
|
||||
if rsp, resp := Client.GetOAuthAccessToken(data); resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else if rsp.RefreshToken == refreshToken {
|
||||
t.Fatal("refresh token did not update")
|
||||
}
|
||||
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
Client.SetOAuthToken(rsp.AccessToken)
|
||||
_, resp = Client.GetMe("")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
data.Set("refresh_token", rsp.RefreshToken)
|
||||
}
|
||||
|
||||
if rsp, resp := Client.GetOAuthAccessToken(data); resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if len(rsp.AccessToken) == 0 {
|
||||
t.Fatal("access token not returned")
|
||||
} else if len(rsp.RefreshToken) == 0 {
|
||||
t.Fatal("refresh token not returned")
|
||||
} else if rsp.RefreshToken == refreshToken {
|
||||
t.Fatal("refresh token did not update")
|
||||
}
|
||||
|
||||
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||
t.Fatal("access token type incorrect")
|
||||
}
|
||||
Client.SetOAuthToken(rsp.AccessToken)
|
||||
_, resp = Client.GetMe("")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
}
|
||||
|
||||
authData := &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: -1}
|
||||
<-th.App.Srv.Store.OAuth().SaveAuthData(authData)
|
||||
|
||||
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||
data.Set("client_id", oauthApp.Id)
|
||||
data.Set("client_secret", oauthApp.ClientSecret)
|
||||
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
|
||||
data.Set("code", authData.Code)
|
||||
data.Del("refresh_token")
|
||||
if _, resp := Client.GetOAuthAccessToken(data); resp.Error == nil {
|
||||
t.Fatal("Should have failed - code is expired")
|
||||
}
|
||||
|
||||
Client.ClearOAuthToken()
|
||||
}
|
||||
|
||||
func TestOAuthComplete(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
|
||||
r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123", Client.HttpClient, "", true)
|
||||
assert.NotNil(t, err)
|
||||
closeBody(r)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Enable = true })
|
||||
r, err = HttpGet(Client.Url+"/login/gitlab/complete?code=123&state=!#$#F@#Yˆ&~ñ", Client.HttpClient, "", true)
|
||||
assert.NotNil(t, err)
|
||||
closeBody(r)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = model.NewId() })
|
||||
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
stateProps["team_id"] = th.BasicTeam.Id
|
||||
stateProps["redirect_to"] = th.App.Config().GitLabSettings.AuthEndpoint
|
||||
|
||||
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
r, err = HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true)
|
||||
assert.NotNil(t, err)
|
||||
closeBody(r)
|
||||
|
||||
stateProps["hash"] = utils.HashSha256(th.App.Config().GitLabSettings.Id)
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
r, err = HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true)
|
||||
assert.NotNil(t, err)
|
||||
closeBody(r)
|
||||
|
||||
// We are going to use mattermost as the provider emulating gitlab
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
oauthApp := &model.OAuthApp{
|
||||
Name: "TestApp5" + model.NewId(),
|
||||
Homepage: "https://nowhere.com",
|
||||
Description: "test",
|
||||
CallbackUrls: []string{
|
||||
Client.Url + "/signup/" + model.SERVICE_GITLAB + "/complete",
|
||||
Client.Url + "/login/" + model.SERVICE_GITLAB + "/complete",
|
||||
},
|
||||
IsTrusted: true,
|
||||
}
|
||||
oauthApp = Client.Must(Client.CreateOAuthApp(oauthApp)).(*model.OAuthApp)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = oauthApp.Id })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Secret = oauthApp.ClientSecret })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.TokenEndpoint = Client.Url + "/oauth/access_token" })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.UserApiEndpoint = Client.ApiUrl + "/users/me" })
|
||||
|
||||
provider := &MattermostTestProvider{}
|
||||
|
||||
authRequest := &model.AuthorizeRequest{
|
||||
ResponseType: model.AUTHCODE_RESPONSE_TYPE,
|
||||
ClientId: oauthApp.Id,
|
||||
RedirectUri: oauthApp.CallbackUrls[0],
|
||||
Scope: "all",
|
||||
State: "123",
|
||||
}
|
||||
|
||||
redirect, resp := Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ := url.Parse(redirect)
|
||||
|
||||
code := rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
|
||||
delete(stateProps, "team_id")
|
||||
stateProps["redirect_to"] = th.App.Config().GitLabSettings.AuthEndpoint
|
||||
stateProps["hash"] = utils.HashSha256(th.App.Config().GitLabSettings.Id)
|
||||
stateProps["redirect_to"] = "/oauth/authorize"
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
einterfaces.RegisterOauthProvider(model.SERVICE_GITLAB, provider)
|
||||
|
||||
redirect, resp = Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ = url.Parse(redirect)
|
||||
|
||||
code = rurl.Query().Get("code")
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
if result := <-th.App.Srv.Store.User().UpdateAuthData(
|
||||
th.BasicUser.Id, model.SERVICE_GITLAB, &th.BasicUser.Email, th.BasicUser.Email, true); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
}
|
||||
|
||||
redirect, resp = Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ = url.Parse(redirect)
|
||||
|
||||
code = rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
redirect, resp = Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ = url.Parse(redirect)
|
||||
|
||||
code = rurl.Query().Get("code")
|
||||
delete(stateProps, "action")
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
|
||||
redirect, resp = Client.AuthorizeOAuthApp(authRequest)
|
||||
CheckNoError(t, resp)
|
||||
rurl, _ = url.Parse(redirect)
|
||||
|
||||
code = rurl.Query().Get("code")
|
||||
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
|
||||
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
|
||||
closeBody(r)
|
||||
}
|
||||
}
|
||||
|
||||
func HttpGet(url string, httpClient *http.Client, authToken string, followRedirect bool) (*http.Response, *model.AppError) {
|
||||
rq, _ := http.NewRequest("GET", url, nil)
|
||||
rq.Close = true
|
||||
|
||||
if len(authToken) > 0 {
|
||||
rq.Header.Set(model.HEADER_AUTH, authToken)
|
||||
}
|
||||
|
||||
if !followRedirect {
|
||||
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
}
|
||||
|
||||
if rp, err := httpClient.Do(rq); err != nil {
|
||||
return nil, model.NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
|
||||
} else if rp.StatusCode == 304 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode == 307 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode >= 300 {
|
||||
defer closeBody(rp)
|
||||
return rp, model.AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return rp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r != nil && r.Body != nil {
|
||||
ioutil.ReadAll(r.Body)
|
||||
r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type MattermostTestProvider struct {
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetIdentifier() string {
|
||||
return model.SERVICE_GITLAB
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader) *model.User {
|
||||
return model.UserFromJson(data)
|
||||
}
|
||||
|
||||
func (m *MattermostTestProvider) GetAuthDataFromJson(data io.Reader) string {
|
||||
authData := model.UserFromJson(data)
|
||||
return authData.Email
|
||||
}
|
||||
|
||||
@@ -4,10 +4,16 @@
|
||||
package api4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
func TestWebSocket(t *testing.T) {
|
||||
@@ -71,3 +77,384 @@ func TestWebSocket(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebSocketEvent(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
omitUser := make(map[string]bool, 1)
|
||||
omitUser["somerandomid"] = true
|
||||
evt1 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", th.BasicChannel.Id, "", omitUser)
|
||||
evt1.Add("user_id", "somerandomid")
|
||||
th.App.Publish(evt1)
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
stop := make(chan bool)
|
||||
eventHit := false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_TYPING && resp.Data["user_id"].(string) == "somerandomid" {
|
||||
eventHit = true
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if !eventHit {
|
||||
t.Fatal("did not receive typing event")
|
||||
}
|
||||
|
||||
evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil)
|
||||
th.App.Publish(evt2)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
eventHit = false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_TYPING {
|
||||
eventHit = true
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if eventHit {
|
||||
t.Fatal("got typing event for bad channel id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDirectChannelWithSocket(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
user2 := th.BasicUser2
|
||||
|
||||
users := make([]*model.User, 0)
|
||||
users = append(users, user2)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
users = append(users, th.CreateUser())
|
||||
}
|
||||
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
wsr := <-WebSocketClient.EventChannel
|
||||
if wsr.Event != model.WEBSOCKET_EVENT_HELLO {
|
||||
t.Fatal("missing hello")
|
||||
}
|
||||
|
||||
stop := make(chan bool)
|
||||
count := 0
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case wsr := <-WebSocketClient.EventChannel:
|
||||
if wsr != nil && wsr.Event == model.WEBSOCKET_EVENT_DIRECT_ADDED {
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, user := range users {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, resp := Client.CreateDirectChannel(th.BasicUser.Id, user.Id); resp.Error != nil {
|
||||
t.Fatal("failed to create DM channel")
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(5000 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if count != len(users) {
|
||||
t.Fatal("We didn't get the proper amount of direct_added messages")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWebsocketOriginSecurity(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
url := fmt.Sprintf("ws://localhost:%v", th.App.Srv.ListenAddr.Port)
|
||||
|
||||
// Should fail because origin doesn't match
|
||||
_, _, err := websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!")
|
||||
}
|
||||
|
||||
// We are not a browser so we can spoof this just fine
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port)},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should succeed now because open CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "*" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should succeed now because matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.evil.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should fail because non-matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{"http://www.evil.com"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin contain AllowCorsFrom")
|
||||
}
|
||||
|
||||
// Should fail because non-matching CORS
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" })
|
||||
_, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX+"/websocket", http.Header{
|
||||
"Origin": []string{"http://www.good.co"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "" })
|
||||
}
|
||||
|
||||
func TestWebSocketStatuses(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
Client := th.Client
|
||||
WebSocketClient, err := th.CreateWebSocketClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer WebSocketClient.Close()
|
||||
WebSocketClient.Listen()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
|
||||
t.Fatal("should have responded OK to authentication challenge")
|
||||
}
|
||||
|
||||
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
|
||||
ruser := Client.Must(Client.CreateUser(&user)).(*model.User)
|
||||
th.LinkUserToTeam(ruser, rteam)
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
|
||||
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
|
||||
ruser2 := Client.Must(Client.CreateUser(&user2)).(*model.User)
|
||||
th.LinkUserToTeam(ruser2, rteam)
|
||||
store.Must(th.App.Srv.Store.User().VerifyEmail(ruser2.Id))
|
||||
|
||||
Client.Login(user.Email, user.Password)
|
||||
|
||||
th.LoginBasic2()
|
||||
|
||||
WebSocketClient2, err2 := th.CreateWebSocketClient()
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
}
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
WebSocketClient.GetStatuses()
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
for _, status := range resp.Data {
|
||||
if status != model.STATUS_OFFLINE && status != model.STATUS_AWAY && status != model.STATUS_ONLINE && status != model.STATUS_DND {
|
||||
t.Fatalf("one of the statuses had an invalid value status=%v", status)
|
||||
}
|
||||
}
|
||||
|
||||
if status, ok := resp.Data[th.BasicUser2.Id]; !ok {
|
||||
t.Log(resp.Data)
|
||||
t.Fatal("should have had user status")
|
||||
} else if status != model.STATUS_ONLINE {
|
||||
t.Log(status)
|
||||
t.Fatal("status should have been online")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{th.BasicUser2.Id})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
for _, status := range resp.Data {
|
||||
if status != model.STATUS_OFFLINE && status != model.STATUS_AWAY && status != model.STATUS_ONLINE {
|
||||
t.Fatal("one of the statuses had an invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
if status, ok := resp.Data[th.BasicUser2.Id]; !ok {
|
||||
t.Log(len(resp.Data))
|
||||
t.Fatal("should have had user status")
|
||||
} else if status != model.STATUS_ONLINE {
|
||||
t.Log(status)
|
||||
t.Fatal("status should have been online")
|
||||
} else if len(resp.Data) != 1 {
|
||||
t.Fatal("only 1 status should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{ruser2.Id, "junk"})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
if len(resp.Data) != 2 {
|
||||
t.Fatal("2 statuses should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketClient.GetStatusesByIds([]string{})
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error == nil {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
t.Fatal("should have errored - empty user ids")
|
||||
}
|
||||
|
||||
WebSocketClient2.Close()
|
||||
|
||||
th.App.SetStatusAwayIfNeeded(th.BasicUser.Id, false)
|
||||
|
||||
awayTimeout := *th.App.Config().TeamSettings.UserStatusAwayTimeout
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.UserStatusAwayTimeout = awayTimeout })
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.UserStatusAwayTimeout = 1 })
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
th.App.SetStatusAwayIfNeeded(th.BasicUser.Id, false)
|
||||
th.App.SetStatusOnline(th.BasicUser.Id, "junk", false)
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
WebSocketClient.GetStatuses()
|
||||
if resp := <-WebSocketClient.ResponseChannel; resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
} else {
|
||||
if resp.SeqReply != WebSocketClient.Sequence-1 {
|
||||
t.Fatal("bad sequence number")
|
||||
}
|
||||
|
||||
if _, ok := resp.Data[th.BasicUser2.Id]; ok {
|
||||
t.Fatal("should not have had user status")
|
||||
}
|
||||
}
|
||||
|
||||
stop := make(chan bool)
|
||||
onlineHit := false
|
||||
awayHit := false
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resp := <-WebSocketClient.EventChannel:
|
||||
if resp.Event == model.WEBSOCKET_EVENT_STATUS_CHANGE && resp.Data["user_id"].(string) == th.BasicUser.Id {
|
||||
status := resp.Data["status"].(string)
|
||||
if status == model.STATUS_ONLINE {
|
||||
onlineHit = true
|
||||
} else if status == model.STATUS_AWAY {
|
||||
awayHit = true
|
||||
}
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
stop <- true
|
||||
|
||||
if !onlineHit {
|
||||
t.Fatal("didn't get online event")
|
||||
}
|
||||
if !awayHit {
|
||||
t.Fatal("didn't get away event")
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
WebSocketClient.Close()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
type AutoChannelCreator struct {
|
||||
client *model.Client
|
||||
client *model.Client4
|
||||
team *model.Team
|
||||
Fuzzy bool
|
||||
DisplayNameLen utils.Range
|
||||
@@ -19,7 +19,7 @@ type AutoChannelCreator struct {
|
||||
ChannelType string
|
||||
}
|
||||
|
||||
func NewAutoChannelCreator(client *model.Client, team *model.Team) *AutoChannelCreator {
|
||||
func NewAutoChannelCreator(client *model.Client4, team *model.Team) *AutoChannelCreator {
|
||||
return &AutoChannelCreator{
|
||||
client: client,
|
||||
team: team,
|
||||
@@ -47,14 +47,14 @@ func (cfg *AutoChannelCreator) createRandomChannel() (*model.Channel, bool) {
|
||||
Name: name,
|
||||
Type: cfg.ChannelType}
|
||||
|
||||
println(cfg.client.GetTeamRoute())
|
||||
result, err := cfg.client.CreateChannel(channel)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
println(err.DetailedError)
|
||||
println(cfg.client.GetTeamRoute(cfg.team.Id))
|
||||
channel, resp := cfg.client.CreateChannel(channel)
|
||||
if resp.Error != nil {
|
||||
println(resp.Error.Error())
|
||||
println(resp.Error.DetailedError)
|
||||
return nil, false
|
||||
}
|
||||
return result.Data.(*model.Channel), true
|
||||
return channel, true
|
||||
}
|
||||
|
||||
func (cfg *AutoChannelCreator) CreateTestChannels(num utils.Range) ([]*model.Channel, bool) {
|
||||
|
||||
@@ -16,7 +16,7 @@ type TestEnvironment struct {
|
||||
Environments []TeamEnvironment
|
||||
}
|
||||
|
||||
func CreateTestEnvironmentWithTeams(a *App, client *model.Client, rangeTeams utils.Range, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TestEnvironment, bool) {
|
||||
func CreateTestEnvironmentWithTeams(a *App, client *model.Client4, rangeTeams utils.Range, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TestEnvironment, bool) {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
teamCreator := NewAutoTeamCreator(client)
|
||||
@@ -36,7 +36,6 @@ func CreateTestEnvironmentWithTeams(a *App, client *model.Client, rangeTeams uti
|
||||
return TestEnvironment{}, false
|
||||
}
|
||||
client.LoginById(randomUser.Id, USER_PASSWORD)
|
||||
client.SetTeamId(team.Id)
|
||||
teamEnvironment, err := CreateTestEnvironmentInTeam(a, client, team, rangeChannels, rangeUsers, rangePosts, fuzzy)
|
||||
if !err {
|
||||
return TestEnvironment{}, false
|
||||
@@ -47,7 +46,7 @@ func CreateTestEnvironmentWithTeams(a *App, client *model.Client, rangeTeams uti
|
||||
return environment, true
|
||||
}
|
||||
|
||||
func CreateTestEnvironmentInTeam(a *App, client *model.Client, team *model.Team, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnvironment, bool) {
|
||||
func CreateTestEnvironmentInTeam(a *App, client *model.Client4, team *model.Team, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnvironment, bool) {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
// We need to create at least one user
|
||||
@@ -74,7 +73,7 @@ func CreateTestEnvironmentInTeam(a *App, client *model.Client, team *model.Team,
|
||||
for _, user := range users {
|
||||
for _, channel := range channels {
|
||||
client.LoginById(user.Id, USER_PASSWORD)
|
||||
client.JoinChannel(channel.Id)
|
||||
client.AddChannelMember(channel.Id, user.Id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type AutoPostCreator struct {
|
||||
client *model.Client
|
||||
client *model.Client4
|
||||
channelid string
|
||||
Fuzzy bool
|
||||
TextLength utils.Range
|
||||
@@ -26,7 +26,7 @@ type AutoPostCreator struct {
|
||||
}
|
||||
|
||||
// Automatic poster used for testing
|
||||
func NewAutoPostCreator(client *model.Client, channelid string) *AutoPostCreator {
|
||||
func NewAutoPostCreator(client *model.Client4, channelid string) *AutoPostCreator {
|
||||
return &AutoPostCreator{
|
||||
client: client,
|
||||
channelid: channelid,
|
||||
@@ -56,7 +56,7 @@ func (cfg *AutoPostCreator) UploadTestFile() ([]string, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
resp, appErr := cfg.client.UploadPostAttachment(data.Bytes(), cfg.channelid, filename)
|
||||
resp, appErr := cfg.client.UploadFile(data.Bytes(), cfg.channelid, filename)
|
||||
if appErr != nil {
|
||||
return nil, false
|
||||
}
|
||||
@@ -85,9 +85,9 @@ func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) {
|
||||
ChannelId: cfg.channelid,
|
||||
Message: postText,
|
||||
FileIds: fileIds}
|
||||
result, err2 := cfg.client.CreatePost(post)
|
||||
rpost, err2 := cfg.client.CreatePost(post)
|
||||
if err2 != nil {
|
||||
return nil, false
|
||||
}
|
||||
return result.Data.(*model.Post), true
|
||||
return rpost, true
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ type TeamEnvironment struct {
|
||||
}
|
||||
|
||||
type AutoTeamCreator struct {
|
||||
client *model.Client
|
||||
client *model.Client4
|
||||
Fuzzy bool
|
||||
NameLength utils.Range
|
||||
NameCharset string
|
||||
@@ -24,7 +24,7 @@ type AutoTeamCreator struct {
|
||||
EmailCharset string
|
||||
}
|
||||
|
||||
func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator {
|
||||
func NewAutoTeamCreator(client *model.Client4) *AutoTeamCreator {
|
||||
return &AutoTeamCreator{
|
||||
client: client,
|
||||
Fuzzy: false,
|
||||
@@ -57,11 +57,10 @@ func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) {
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
result, err := cfg.client.CreateTeam(team)
|
||||
createdTeam, err := cfg.client.CreateTeam(team)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
createdTeam := result.Data.(*model.Team)
|
||||
return createdTeam, true
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
type AutoUserCreator struct {
|
||||
app *App
|
||||
client *model.Client
|
||||
client *model.Client4
|
||||
team *model.Team
|
||||
EmailLength utils.Range
|
||||
EmailCharset string
|
||||
@@ -21,7 +21,7 @@ type AutoUserCreator struct {
|
||||
Fuzzy bool
|
||||
}
|
||||
|
||||
func NewAutoUserCreator(a *App, client *model.Client, team *model.Team) *AutoUserCreator {
|
||||
func NewAutoUserCreator(a *App, client *model.Client4, team *model.Team) *AutoUserCreator {
|
||||
return &AutoUserCreator{
|
||||
app: a,
|
||||
client: client,
|
||||
@@ -35,21 +35,19 @@ func NewAutoUserCreator(a *App, client *model.Client, team *model.Team) *AutoUse
|
||||
}
|
||||
|
||||
// Basic test team and user so you always know one
|
||||
func (a *App) CreateBasicUser(client *model.Client) *model.AppError {
|
||||
result, _ := client.FindTeamByName(BTEST_TEAM_NAME)
|
||||
if !result.Data.(bool) {
|
||||
func (a *App) CreateBasicUser(client *model.Client4) *model.AppError {
|
||||
found, _ := client.TeamExists(BTEST_TEAM_NAME, "")
|
||||
if !found {
|
||||
newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
|
||||
result, err := client.CreateTeam(newteam)
|
||||
if err != nil {
|
||||
return err
|
||||
basicteam, resp := client.CreateTeam(newteam)
|
||||
if resp.Error != nil {
|
||||
return resp.Error
|
||||
}
|
||||
basicteam := result.Data.(*model.Team)
|
||||
newuser := &model.User{Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
|
||||
result, err = client.CreateUser(newuser, "")
|
||||
if err != nil {
|
||||
return err
|
||||
ruser, resp := client.CreateUser(newuser)
|
||||
if resp.Error != nil {
|
||||
return resp.Error
|
||||
}
|
||||
ruser := result.Data.(*model.User)
|
||||
store.Must(a.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
store.Must(a.Srv.Store.Team().SaveMember(&model.TeamMember{TeamId: basicteam.Id, UserId: ruser.Id}, *a.Config().TeamSettings.MaxUsersPerTeam))
|
||||
}
|
||||
@@ -72,14 +70,12 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
|
||||
Nickname: userName,
|
||||
Password: USER_PASSWORD}
|
||||
|
||||
result, err := cfg.client.CreateUserWithInvite(user, "", "", cfg.team.InviteId)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
ruser, resp := cfg.client.CreateUserWithInviteId(user, cfg.team.InviteId)
|
||||
if resp.Error != nil {
|
||||
mlog.Error(resp.Error.Error())
|
||||
return nil, false
|
||||
}
|
||||
|
||||
ruser := result.Data.(*model.User)
|
||||
|
||||
status := &model.Status{UserId: ruser.Id, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
if result := <-cfg.app.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
mlog.Error(result.Err.Error())
|
||||
@@ -89,7 +85,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
|
||||
// We need to cheat to verify the user's email
|
||||
store.Must(cfg.app.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
|
||||
return result.Data.(*model.User), true
|
||||
return ruser, true
|
||||
}
|
||||
|
||||
func (cfg *AutoUserCreator) CreateTestUsers(num utils.Range) ([]*model.User, bool) {
|
||||
|
||||
@@ -159,7 +159,7 @@ func (me *LoadTestProvider) SetupCommand(a *App, args *model.CommandArgs, messag
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
}
|
||||
}
|
||||
client := model.NewClient(args.SiteURL)
|
||||
client := model.NewAPIv4Client(args.SiteURL)
|
||||
|
||||
if doTeams {
|
||||
if err := a.CreateBasicUser(client); err != nil {
|
||||
@@ -193,7 +193,6 @@ func (me *LoadTestProvider) SetupCommand(a *App, args *model.CommandArgs, messag
|
||||
}
|
||||
|
||||
client.MockSession(args.Session.Token)
|
||||
client.SetTeamId(args.TeamId)
|
||||
CreateTestEnvironmentInTeam(
|
||||
a,
|
||||
client,
|
||||
@@ -228,8 +227,7 @@ func (me *LoadTestProvider) UsersCommand(a *App, args *model.CommandArgs, messag
|
||||
team = tr.Data.(*model.Team)
|
||||
}
|
||||
|
||||
client := model.NewClient(args.SiteURL)
|
||||
client.SetTeamId(team.Id)
|
||||
client := model.NewAPIv4Client(args.SiteURL)
|
||||
userCreator := NewAutoUserCreator(a, client, team)
|
||||
userCreator.Fuzzy = doFuzz
|
||||
userCreator.CreateTestUsers(usersr)
|
||||
@@ -258,8 +256,7 @@ func (me *LoadTestProvider) ChannelsCommand(a *App, args *model.CommandArgs, mes
|
||||
team = tr.Data.(*model.Team)
|
||||
}
|
||||
|
||||
client := model.NewClient(args.SiteURL)
|
||||
client.SetTeamId(team.Id)
|
||||
client := model.NewAPIv4Client(args.SiteURL)
|
||||
client.MockSession(args.Session.Token)
|
||||
channelCreator := NewAutoChannelCreator(client, team)
|
||||
channelCreator.Fuzzy = doFuzz
|
||||
@@ -301,8 +298,7 @@ func (me *LoadTestProvider) PostsCommand(a *App, args *model.CommandArgs, messag
|
||||
}
|
||||
}
|
||||
|
||||
client := model.NewClient(args.SiteURL)
|
||||
client.SetTeamId(args.TeamId)
|
||||
client := model.NewAPIv4Client(args.SiteURL)
|
||||
client.MockSession(args.Session.Token)
|
||||
testPoster := NewAutoPostCreator(client, args.ChannelId)
|
||||
testPoster.Fuzzy = doFuzz
|
||||
|
||||
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"encoding/json"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -181,10 +180,7 @@ func (a *App) trackActivity() {
|
||||
"public_channels_deleted": deletedPublicChannelCount,
|
||||
"private_channels_deleted": deletedPrivateChannelCount,
|
||||
"posts": postsCount,
|
||||
"used_apiv3": atomic.LoadInt32(model.UsedApiV3) == 1,
|
||||
})
|
||||
|
||||
atomic.StoreInt32(model.UsedApiV3, 0)
|
||||
}
|
||||
|
||||
func (a *App) trackConfig() {
|
||||
@@ -199,7 +195,6 @@ func (a *App) trackConfig() {
|
||||
"enable_only_admin_integrations": *cfg.ServiceSettings.EnableOnlyAdminIntegrations,
|
||||
"enable_post_username_override": cfg.ServiceSettings.EnablePostUsernameOverride,
|
||||
"enable_post_icon_override": cfg.ServiceSettings.EnablePostIconOverride,
|
||||
"enable_apiv3": *cfg.ServiceSettings.EnableAPIv3,
|
||||
"enable_user_access_tokens": *cfg.ServiceSettings.EnableUserAccessTokens,
|
||||
"enable_custom_emoji": *cfg.ServiceSettings.EnableCustomEmoji,
|
||||
"enable_emoji_picker": *cfg.ServiceSettings.EnableEmojiPicker,
|
||||
|
||||
@@ -295,11 +295,6 @@ func (a *App) GeneratePublicLink(siteURL string, info *model.FileInfo) string {
|
||||
return fmt.Sprintf("%s/files/%v/public?h=%s", siteURL, info.Id, hash)
|
||||
}
|
||||
|
||||
func (a *App) GeneratePublicLinkV3(siteURL string, info *model.FileInfo) string {
|
||||
hash := GeneratePublicLinkHash(info.Id, *a.Config().FileSettings.PublicLinkSalt)
|
||||
return fmt.Sprintf("%s%s/public/files/%v/get?h=%s", siteURL, model.API_URL_SUFFIX_V3, info.Id, hash)
|
||||
}
|
||||
|
||||
func GeneratePublicLinkHash(fileId, salt string) string {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(salt))
|
||||
|
||||
@@ -5,10 +5,16 @@ package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestGeneratePublicLinkHash(t *testing.T) {
|
||||
@@ -100,3 +106,58 @@ func TestDoUploadFile(t *testing.T) {
|
||||
t.Fatal("stored file at incorrect path", info4.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfoForFilename(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
post := th.BasicPost
|
||||
teamId := th.BasicTeam.Id
|
||||
|
||||
info := th.App.GetInfoForFilename(post, teamId, "sometestfile")
|
||||
assert.Nil(t, info, "Test bad filename")
|
||||
|
||||
info = th.App.GetInfoForFilename(post, teamId, "/somechannel/someuser/someid/somefile.png")
|
||||
assert.Nil(t, info, "Test non-existent file")
|
||||
}
|
||||
|
||||
func TestFindTeamIdForFilename(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
teamId := th.App.FindTeamIdForFilename(th.BasicPost, fmt.Sprintf("/%v/%v/%v/blargh.png", th.BasicChannel.Id, th.BasicUser.Id, "someid"))
|
||||
assert.Equal(t, th.BasicTeam.Id, teamId)
|
||||
|
||||
_, err := th.App.CreateTeamWithUser(&model.Team{Email: th.BasicUser.Email, Name: "zz" + model.NewId(), DisplayName: "Joram's Test Team", Type: model.TEAM_OPEN}, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId = th.App.FindTeamIdForFilename(th.BasicPost, fmt.Sprintf("/%v/%v/%v/blargh.png", th.BasicChannel.Id, th.BasicUser.Id, "someid"))
|
||||
assert.Equal(t, "", teamId)
|
||||
}
|
||||
|
||||
func TestMigrateFilenamesToFileInfos(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
post := th.BasicPost
|
||||
infos := th.App.MigrateFilenamesToFileInfos(post)
|
||||
assert.Equal(t, 0, len(infos))
|
||||
|
||||
post.Filenames = []string{fmt.Sprintf("/%v/%v/%v/blargh.png", th.BasicChannel.Id, th.BasicUser.Id, "someid")}
|
||||
infos = th.App.MigrateFilenamesToFileInfos(post)
|
||||
assert.Equal(t, 0, len(infos))
|
||||
|
||||
path, _ := utils.FindDir("tests")
|
||||
file, fileErr := os.Open(filepath.Join(path, "test.png"))
|
||||
require.Nil(t, fileErr)
|
||||
defer file.Close()
|
||||
|
||||
fpath := fmt.Sprintf("/teams/%v/channels/%v/users/%v/%v/test.png", th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, "someid")
|
||||
_, err := th.App.WriteFile(file, fpath)
|
||||
require.Nil(t, err)
|
||||
rpost, err := th.App.CreatePost(&model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/test.png", th.BasicChannel.Id, th.BasicUser.Id, "someid")}}, th.BasicChannel, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
infos = th.App.MigrateFilenamesToFileInfos(rpost)
|
||||
assert.Equal(t, 1, len(infos))
|
||||
}
|
||||
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestJoinChannel(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
channel := th.CreatePublicChannel()
|
||||
|
||||
cmd.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
@@ -29,10 +29,10 @@ func TestJoinChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveChannel(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
channel := th.CreatePublicChannel()
|
||||
|
||||
cmd.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
@@ -46,12 +46,11 @@ func TestRemoveChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMoveChannel(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
client := th.BasicClient
|
||||
team1 := th.BasicTeam
|
||||
team2 := th.CreateTeam(client)
|
||||
team2 := th.CreateTeam()
|
||||
user1 := th.BasicUser
|
||||
th.LinkUserToTeam(user1, team2)
|
||||
channel := th.BasicChannel
|
||||
@@ -73,11 +72,11 @@ func TestMoveChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListChannels(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
th.BasicClient.Must(th.BasicClient.DeleteChannel(channel.Id))
|
||||
channel := th.CreatePublicChannel()
|
||||
th.Client.Must(th.Client.DeleteChannel(channel.Id))
|
||||
|
||||
output := cmd.CheckCommand(t, "channel", "list", th.BasicTeam.Name)
|
||||
|
||||
@@ -91,11 +90,11 @@ func TestListChannels(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestoreChannel(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
th.BasicClient.Must(th.BasicClient.DeleteChannel(channel.Id))
|
||||
channel := th.CreatePublicChannel()
|
||||
th.Client.Must(th.Client.DeleteChannel(channel.Id))
|
||||
|
||||
cmd.CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name)
|
||||
|
||||
@@ -104,7 +103,7 @@ func TestRestoreChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
id := model.NewId()
|
||||
|
||||
@@ -6,13 +6,13 @@ package commands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestAssignRole(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cmd.CheckCommand(t, "roles", "system_admin", th.BasicUser.Email)
|
||||
|
||||
@@ -6,13 +6,13 @@ package commands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSampledataBadParameters(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// should fail because you need at least 1 worker
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
@@ -104,8 +103,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
|
||||
return serverErr
|
||||
}
|
||||
|
||||
api4.Init(a, a.Srv.Router, false)
|
||||
api3 := api.Init(a, a.Srv.Router)
|
||||
api := api4.Init(a, a.Srv.Router)
|
||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||
web.NewWeb(a, a.Srv.Router)
|
||||
|
||||
@@ -135,7 +133,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
|
||||
|
||||
// If we allow testing then listen for manual testing URL hits
|
||||
if a.Config().ServiceSettings.EnableTesting {
|
||||
manualtesting.Init(api3)
|
||||
manualtesting.Init(api)
|
||||
}
|
||||
|
||||
a.EnsureDiagnosticId()
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestCreateTeam(t *testing.T) {
|
||||
th := api.Setup().InitSystemAdmin()
|
||||
th := api4.Setup().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
id := model.NewId()
|
||||
@@ -22,7 +22,7 @@ func TestCreateTeam(t *testing.T) {
|
||||
|
||||
cmd.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName)
|
||||
|
||||
found := th.SystemAdminClient.Must(th.SystemAdminClient.FindTeamByName(name)).Data.(bool)
|
||||
found := th.SystemAdminClient.Must(th.SystemAdminClient.TeamExists(name, "")).(bool)
|
||||
|
||||
if !found {
|
||||
t.Fatal("Failed to create Team")
|
||||
@@ -30,12 +30,12 @@ func TestCreateTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestJoinTeam(t *testing.T) {
|
||||
th := api.Setup().InitSystemAdmin().InitBasic()
|
||||
th := api4.Setup().InitSystemAdmin().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cmd.CheckCommand(t, "team", "add", th.SystemAdminTeam.Name, th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "team", "add", th.BasicTeam.Name, th.BasicUser.Email)
|
||||
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User)
|
||||
|
||||
found := false
|
||||
|
||||
@@ -52,12 +52,12 @@ func TestJoinTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLeaveTeam(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cmd.CheckCommand(t, "team", "remove", th.BasicTeam.Name, th.BasicUser.Email)
|
||||
|
||||
profiles := th.BasicClient.Must(th.BasicClient.GetProfilesInTeam(th.BasicTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
profiles := th.Client.Must(th.Client.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User)
|
||||
|
||||
found := false
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestLeaveTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListTeams(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
id := model.NewId()
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -60,8 +59,7 @@ func webClientTestsCmdF(command *cobra.Command, args []string) error {
|
||||
return serverErr
|
||||
}
|
||||
|
||||
api4.Init(a, a.Srv.Router, false)
|
||||
api.Init(a, a.Srv.Router)
|
||||
api4.Init(a, a.Srv.Router)
|
||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||
a.UpdateConfig(setupClientTests)
|
||||
runWebClientTests()
|
||||
@@ -82,8 +80,7 @@ func serverForWebClientTestsCmdF(command *cobra.Command, args []string) error {
|
||||
return serverErr
|
||||
}
|
||||
|
||||
api4.Init(a, a.Srv.Router, false)
|
||||
api.Init(a, a.Srv.Router)
|
||||
api4.Init(a, a.Srv.Router)
|
||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||
a.UpdateConfig(setupClientTests)
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ package commands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateUserWithTeam(t *testing.T) {
|
||||
th := api.Setup().InitSystemAdmin()
|
||||
th := api4.Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
id := model.NewId()
|
||||
@@ -22,9 +22,9 @@ func TestCreateUserWithTeam(t *testing.T) {
|
||||
|
||||
cmd.CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username)
|
||||
|
||||
cmd.CheckCommand(t, "team", "add", th.SystemAdminTeam.Id, email)
|
||||
cmd.CheckCommand(t, "team", "add", th.BasicTeam.Id, email)
|
||||
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User)
|
||||
|
||||
found := false
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestCreateUserWithTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUserWithoutTeam(t *testing.T) {
|
||||
th := api.Setup()
|
||||
th := api4.Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
id := model.NewId()
|
||||
@@ -61,18 +61,18 @@ func TestCreateUserWithoutTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResetPassword(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cmd.CheckCommand(t, "user", "password", th.BasicUser.Email, "password2")
|
||||
|
||||
th.BasicClient.Logout()
|
||||
th.Client.Logout()
|
||||
th.BasicUser.Password = "password2"
|
||||
th.LoginBasic()
|
||||
}
|
||||
|
||||
func TestMakeUserActiveAndInactive(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// first inactivate the user
|
||||
@@ -83,7 +83,7 @@ func TestMakeUserActiveAndInactive(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChangeUserEmail(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
newEmail := model.NewId() + "@mattermost-test.com"
|
||||
|
||||
@@ -12,28 +12,29 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"github.com/mattermost/mattermost-server/web"
|
||||
)
|
||||
|
||||
type TestEnvironment struct {
|
||||
Params map[string][]string
|
||||
Client *model.Client
|
||||
Client *model.Client4
|
||||
CreatedTeamId string
|
||||
CreatedUserId string
|
||||
Context *api.Context
|
||||
Context *web.Context
|
||||
Writer http.ResponseWriter
|
||||
Request *http.Request
|
||||
}
|
||||
|
||||
func Init(api3 *api.API) {
|
||||
api3.BaseRoutes.Root.Handle("/manualtest", api3.AppHandler(manualTest)).Methods("GET")
|
||||
func Init(api4 *api4.API) {
|
||||
api4.BaseRoutes.Root.Handle("/manualtest", api4.ApiHandler(manualTest)).Methods("GET")
|
||||
}
|
||||
|
||||
func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func manualTest(c *web.Context, w http.ResponseWriter, r *http.Request) {
|
||||
// Let the world know
|
||||
mlog.Info("Setting up for manual test...")
|
||||
|
||||
@@ -56,7 +57,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Create a client for tests to use
|
||||
client := model.NewClient("http://localhost" + *c.App.Config().ServiceSettings.ListenAddress)
|
||||
client := model.NewAPIv4Client("http://localhost" + *c.App.Config().ServiceSettings.ListenAddress)
|
||||
|
||||
// Check for username parameter and create a user if present
|
||||
username, ok1 := params["username"]
|
||||
@@ -95,22 +96,21 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||
Nickname: username[0],
|
||||
Password: app.USER_PASSWORD}
|
||||
|
||||
result, err := client.CreateUser(user, "")
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
user, resp := client.CreateUser(user)
|
||||
if resp.Error != nil {
|
||||
c.Err = resp.Error
|
||||
return
|
||||
}
|
||||
|
||||
<-c.App.Srv.Store.User().VerifyEmail(result.Data.(*model.User).Id)
|
||||
<-c.App.Srv.Store.Team().SaveMember(&model.TeamMember{TeamId: teamID, UserId: result.Data.(*model.User).Id}, *c.App.Config().TeamSettings.MaxUsersPerTeam)
|
||||
<-c.App.Srv.Store.User().VerifyEmail(user.Id)
|
||||
<-c.App.Srv.Store.Team().SaveMember(&model.TeamMember{TeamId: teamID, UserId: user.Id}, *c.App.Config().TeamSettings.MaxUsersPerTeam)
|
||||
|
||||
newuser := result.Data.(*model.User)
|
||||
userID = newuser.Id
|
||||
userID = user.Id
|
||||
|
||||
// Login as user to generate auth token
|
||||
_, err = client.LoginById(newuser.Id, app.USER_PASSWORD)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
_, resp = client.LoginById(user.Id, app.USER_PASSWORD)
|
||||
if resp.Error != nil {
|
||||
c.Err = resp.Error
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,6 @@ func testAutoLink(env TestEnvironment) *model.AppError {
|
||||
post := &model.Post{
|
||||
ChannelId: channelID,
|
||||
Message: LINK_POST_TEXT}
|
||||
_, err2 := env.Client.CreatePost(post)
|
||||
return err2
|
||||
_, resp := env.Client.CreatePost(post)
|
||||
return resp.Error
|
||||
}
|
||||
|
||||
2379
model/client.go
2379
model/client.go
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -13,6 +13,33 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_REQUEST_ID = "X-Request-ID"
|
||||
HEADER_VERSION_ID = "X-Version-ID"
|
||||
HEADER_CLUSTER_ID = "X-Cluster-ID"
|
||||
HEADER_ETAG_SERVER = "ETag"
|
||||
HEADER_ETAG_CLIENT = "If-None-Match"
|
||||
HEADER_FORWARDED = "X-Forwarded-For"
|
||||
HEADER_REAL_IP = "X-Real-IP"
|
||||
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
|
||||
HEADER_TOKEN = "token"
|
||||
HEADER_BEARER = "BEARER"
|
||||
HEADER_AUTH = "Authorization"
|
||||
HEADER_REQUESTED_WITH = "X-Requested-With"
|
||||
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
|
||||
STATUS = "status"
|
||||
STATUS_OK = "OK"
|
||||
STATUS_FAIL = "FAIL"
|
||||
STATUS_REMOVE = "REMOVE"
|
||||
|
||||
CLIENT_DIR = "client"
|
||||
|
||||
API_URL_SUFFIX_V1 = "/api/v1"
|
||||
API_URL_SUFFIX_V4 = "/api/v4"
|
||||
API_URL_SUFFIX = API_URL_SUFFIX_V4
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
@@ -32,6 +59,24 @@ type Client4 struct {
|
||||
AuthType string
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r.Body != nil {
|
||||
ioutil.ReadAll(r.Body)
|
||||
r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Must is a convenience function used for testing.
|
||||
func (c *Client4) Must(result interface{}, resp *Response) interface{} {
|
||||
if resp.Error != nil {
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(resp.Error)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func NewAPIv4Client(url string) *Client4 {
|
||||
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", ""}
|
||||
}
|
||||
@@ -64,6 +109,11 @@ func BuildResponse(r *http.Response) *Response {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client4) MockSession(sessionToken string) {
|
||||
c.AuthToken = sessionToken
|
||||
c.AuthType = HEADER_BEARER
|
||||
}
|
||||
|
||||
func (c *Client4) SetOAuthToken(token string) {
|
||||
c.AuthToken = token
|
||||
c.AuthType = HEADER_TOKEN
|
||||
@@ -2966,6 +3016,28 @@ func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetOAuthAccessToken is a test helper function for the OAuth access token endpoint.
|
||||
func (c *Client4) GetOAuthAccessToken(data url.Values) (*AccessResponse, *Response) {
|
||||
rq, _ := http.NewRequest(http.MethodPost, c.Url+"/oauth/access_token", strings.NewReader(data.Encode()))
|
||||
rq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
rq.Close = true
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
|
||||
return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.Url+"/oauth/access_token", "model.client.connecting.app_error", nil, err.Error(), 403)}
|
||||
} else {
|
||||
defer closeBody(rp)
|
||||
if rp.StatusCode >= 300 {
|
||||
return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
|
||||
} else {
|
||||
return AccessResponseFromJson(rp.Body), BuildResponse(rp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elasticsearch Section
|
||||
|
||||
// TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured
|
||||
|
||||
@@ -184,7 +184,6 @@ type ServiceSettings struct {
|
||||
EnableOnlyAdminIntegrations *bool
|
||||
EnablePostUsernameOverride bool
|
||||
EnablePostIconOverride bool
|
||||
EnableAPIv3 *bool
|
||||
EnableLinkPreviews *bool
|
||||
EnableTesting bool
|
||||
EnableDeveloper *bool
|
||||
@@ -244,10 +243,6 @@ func (s *ServiceSettings) SetDefaults() {
|
||||
s.ListenAddress = NewString(SERVICE_SETTINGS_DEFAULT_LISTEN_AND_ADDRESS)
|
||||
}
|
||||
|
||||
if s.EnableAPIv3 == nil {
|
||||
s.EnableAPIv3 = NewBool(true)
|
||||
}
|
||||
|
||||
if s.EnableLinkPreviews == nil {
|
||||
s.EnableLinkPreviews = NewBool(false)
|
||||
}
|
||||
|
||||
@@ -39,15 +39,15 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
|
||||
// NewWebSocketClientWithDialer constructs a new WebSocket client with convenience
|
||||
// methods for talking to the server using a custom dialer.
|
||||
func NewWebSocketClientWithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) {
|
||||
conn, _, err := dialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil)
|
||||
conn, _, err := dialer.Dial(url+API_URL_SUFFIX+"/websocket", nil)
|
||||
if err != nil {
|
||||
return nil, NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
client := &WebSocketClient{
|
||||
url,
|
||||
url + API_URL_SUFFIX_V3,
|
||||
url + API_URL_SUFFIX_V3 + "/users/websocket",
|
||||
url + API_URL_SUFFIX,
|
||||
url + API_URL_SUFFIX + "/websocket",
|
||||
conn,
|
||||
authToken,
|
||||
1,
|
||||
@@ -74,30 +74,7 @@ func NewWebSocketClient4(url, authToken string) (*WebSocketClient, *AppError) {
|
||||
// NewWebSocketClient4WithDialer constructs a new WebSocket client with convenience
|
||||
// methods for talking to the server using a custom dialer. Uses the v4 endpoint.
|
||||
func NewWebSocketClient4WithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) {
|
||||
conn, _, err := dialer.Dial(url+API_URL_SUFFIX+"/websocket", nil)
|
||||
if err != nil {
|
||||
return nil, NewAppError("NewWebSocketClient4", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
client := &WebSocketClient{
|
||||
url,
|
||||
url + API_URL_SUFFIX,
|
||||
url + API_URL_SUFFIX + "/websocket",
|
||||
conn,
|
||||
authToken,
|
||||
1,
|
||||
make(chan bool, 1),
|
||||
make(chan *WebSocketEvent, 100),
|
||||
make(chan *WebSocketResponse, 100),
|
||||
nil,
|
||||
nil,
|
||||
}
|
||||
|
||||
client.configurePingHandling()
|
||||
|
||||
client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken})
|
||||
|
||||
return client, nil
|
||||
return NewWebSocketClientWithDialer(dialer, url, authToken)
|
||||
}
|
||||
|
||||
func (wsc *WebSocketClient) Connect() *AppError {
|
||||
|
||||
@@ -448,7 +448,6 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
|
||||
props["WebsocketURL"] = strings.TrimRight(*c.ServiceSettings.WebsocketURL, "/")
|
||||
props["SiteName"] = c.TeamSettings.SiteName
|
||||
props["EnableTeamCreation"] = strconv.FormatBool(*c.TeamSettings.EnableTeamCreation)
|
||||
props["EnableAPIv3"] = strconv.FormatBool(*c.ServiceSettings.EnableAPIv3)
|
||||
props["EnableUserCreation"] = strconv.FormatBool(c.TeamSettings.EnableUserCreation)
|
||||
props["EnableOpenServer"] = strconv.FormatBool(*c.TeamSettings.EnableOpenServer)
|
||||
props["RestrictDirectMessage"] = *c.TeamSettings.RestrictDirectMessage
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
var ApiClient *model.Client
|
||||
var ApiClient *model.Client4
|
||||
var URL string
|
||||
|
||||
type persistentTestStore struct {
|
||||
@@ -58,7 +58,7 @@ func Setup() *TestHelper {
|
||||
|
||||
NewWeb(a, a.Srv.Router)
|
||||
URL = fmt.Sprintf("http://localhost:%v", a.Srv.ListenAddr.Port)
|
||||
ApiClient = model.NewClient(URL)
|
||||
ApiClient = model.NewAPIv4Client(URL)
|
||||
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
|
||||
@@ -66,7 +66,6 @@ func Setup() *TestHelper {
|
||||
|
||||
a.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.EnableOpenServer = true
|
||||
*cfg.ServiceSettings.EnableAPIv3 = true
|
||||
})
|
||||
|
||||
th := &TestHelper{
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -20,7 +21,7 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
|
||||
if !th.App.Config().ServiceSettings.EnableIncomingWebhooks {
|
||||
_, err := ApiClient.PostToWebhook("123", "123")
|
||||
_, err := http.Post(ApiClient.Url+"/hooks/123", "", strings.NewReader("123"))
|
||||
assert.NotNil(t, err, "should have errored - webhooks turned off")
|
||||
return
|
||||
}
|
||||
@@ -28,7 +29,7 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
hook, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id})
|
||||
require.Nil(t, err)
|
||||
|
||||
url := "/hooks/" + hook.Id
|
||||
url := ApiClient.Url + "/hooks/" + hook.Id
|
||||
|
||||
tooLongText := ""
|
||||
for i := 0; i < 8200; i++ {
|
||||
@@ -37,55 +38,68 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
|
||||
t.Run("WebhookBasics", func(t *testing.T) {
|
||||
payload := "payload={\"text\": \"test text\"}"
|
||||
_, err := ApiClient.DoPost(url, payload, "application/x-www-form-urlencoded")
|
||||
assert.Nil(t, err)
|
||||
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
payload = "payload={\"text\": \"\"}"
|
||||
_, err = ApiClient.DoPost(url, payload, "application/x-www-form-urlencoded")
|
||||
assert.NotNil(t, err, "should have errored - no text to post")
|
||||
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - no text to post")
|
||||
|
||||
payload = "payload={\"text\": \"test text\", \"channel\": \"junk\"}"
|
||||
_, err = ApiClient.DoPost(url, payload, "application/x-www-form-urlencoded")
|
||||
assert.NotNil(t, err, "should have errored - bad channel")
|
||||
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - bad channel")
|
||||
|
||||
payload = "payload={\"text\": \"test text\"}"
|
||||
_, err = ApiClient.DoPost("/hooks/abc123", payload, "application/x-www-form-urlencoded")
|
||||
assert.NotNil(t, err, "should have errored - bad hook")
|
||||
resp, err = http.Post(ApiClient.Url+"/hooks/abc123", "application/x-www-form-urlencoded", strings.NewReader(payload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - bad hook")
|
||||
|
||||
_, err = ApiClient.DoPost(url, "{\"text\":\"this is a test\"}", "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\"this is a test\"}"))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
text := `this is a \"test\"
|
||||
that contains a newline and a tab`
|
||||
_, err = ApiClient.DoPost(url, "{\"text\":\""+text+"\"}", "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\""+text+"\"}"))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", th.BasicChannel.Name), "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", th.BasicChannel.Name)))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"#%s\"}", th.BasicChannel.Name), "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"#%s\"}", th.BasicChannel.Name)))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"@%s\"}", th.BasicUser.Username), "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"@%s\"}", th.BasicUser.Username)))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, "payload={\"text\":\"this is a test\"}", "application/x-www-form-urlencoded")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("payload={\"text\":\"this is a test\"}"))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, "payload={\"text\":\""+text+"\"}", "application/x-www-form-urlencoded")
|
||||
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("payload={\"text\":\""+text+"\"}"))
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
_, err = ApiClient.DoPost(url, "{\"text\":\""+tooLongText+"\"}", "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\""+tooLongText+"\"}"))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
payloadMultiPart := "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"username\"\r\n\r\nwebhook-bot\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nthis is a test :tada:\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--"
|
||||
_, err = ApiClient.DoPost("/hooks/"+hook.Id, payloadMultiPart, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(ApiClient.Url+"/hooks/"+hook.Id, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", strings.NewReader(payloadMultiPart))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("WebhookExperimentReadOnly", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = false })
|
||||
_, err := ApiClient.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", model.DEFAULT_CHANNEL), "application/json")
|
||||
_, err := http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", model.DEFAULT_CHANNEL)))
|
||||
assert.Nil(t, err, "Not read only")
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true })
|
||||
@@ -126,8 +140,9 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := ApiClient.DoPost(url, attachmentPayload, "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err := http.Post(url, "application/json", strings.NewReader(attachmentPayload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
|
||||
attachmentPayload = `{
|
||||
"text": "this is a test",
|
||||
@@ -162,14 +177,16 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
]
|
||||
}`
|
||||
|
||||
_, err = ApiClient.DoPost(url, attachmentPayload, "application/json")
|
||||
assert.Nil(t, err)
|
||||
resp, err = http.Post(url, "application/json", strings.NewReader(attachmentPayload))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("DisableWebhooks", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = false })
|
||||
_, err := ApiClient.DoPost(url, "{\"text\":\"this is a test\"}", "application/json")
|
||||
assert.NotNil(t, err)
|
||||
resp, err := http.Post(url, "application/json", strings.NewReader("{\"text\":\"this is a test\"}"))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, resp.StatusCode == http.StatusNotImplemented)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user