diff options
-rw-r--r-- | api4/system.go | 76 | ||||
-rw-r--r-- | api4/system_test.go | 36 | ||||
-rw-r--r-- | app/license.go | 13 | ||||
-rw-r--r-- | model/client4.go | 46 |
4 files changed, 164 insertions, 7 deletions
diff --git a/api4/system.go b/api4/system.go index 97d8bb7dc..92674419f 100644 --- a/api4/system.go +++ b/api4/system.go @@ -4,6 +4,8 @@ package api4 import ( + "bytes" + "io" "net/http" "runtime" "strconv" @@ -24,6 +26,8 @@ func InitSystem() { BaseRoutes.ApiRoot.Handle("/config/reload", ApiSessionRequired(configReload)).Methods("POST") BaseRoutes.ApiRoot.Handle("/config/client", ApiHandler(getClientConfig)).Methods("GET") + BaseRoutes.ApiRoot.Handle("/license", ApiSessionRequired(addLicense)).Methods("POST") + BaseRoutes.ApiRoot.Handle("/license", ApiSessionRequired(removeLicense)).Methods("DELETE") BaseRoutes.ApiRoot.Handle("/license/client", ApiHandler(getClientLicense)).Methods("GET") BaseRoutes.ApiRoot.Handle("/audits", ApiSessionRequired(getAudits)).Methods("GET") @@ -260,3 +264,75 @@ func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) { w.Header().Set(model.HEADER_ETAG_SERVER, etag) w.Write([]byte(model.MapToJson(clientLicense))) } + +func addLicense(c *Context, w http.ResponseWriter, r *http.Request) { + c.LogAudit("attempt") + + if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + err := r.ParseMultipartForm(*utils.Cfg.FileSettings.MaxFileSize) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + 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() + defer file.Close() + if err != nil { + c.Err = model.NewAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error(), http.StatusBadRequest) + return + } + + buf := bytes.NewBuffer(nil) + io.Copy(buf, file) + + if license, err := 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("attempt") + + if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + if err := app.RemoveLicense(); err != nil { + c.Err = err + return + } + + c.LogAudit("success") + ReturnStatusOK(w) +} diff --git a/api4/system_test.go b/api4/system_test.go index a46e14782..57cc10343 100644 --- a/api4/system_test.go +++ b/api4/system_test.go @@ -356,3 +356,39 @@ func TestPostLog(t *testing.T) { t.Fatal("should return the log message") } } + +func TestUploadLicenseFile(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + ok, resp := Client.UploadLicenseFile([]byte{}) + CheckForbiddenStatus(t, resp) + if ok { + t.Fatal("should fail") + } + + ok, resp = th.SystemAdminClient.UploadLicenseFile([]byte{}) + CheckBadRequestStatus(t, resp) + if ok { + t.Fatal("should fail") + } +} + +func TestRemoveLicenseFile(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + ok, resp := Client.RemoveLicenseFile() + CheckForbiddenStatus(t, resp) + if ok { + t.Fatal("should fail") + } + + ok, resp = th.SystemAdminClient.RemoveLicenseFile() + CheckNoError(t, resp) + if !ok { + t.Fatal("should pass") + } +} diff --git a/app/license.go b/app/license.go index 44b700d5b..8cb76fd6e 100644 --- a/app/license.go +++ b/app/license.go @@ -4,6 +4,7 @@ package app import ( + "net/http" "strings" l4g "github.com/alecthomas/log4go" @@ -49,17 +50,17 @@ func SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) { license = model.LicenseFromJson(strings.NewReader(licenseStr)) if result := <-Srv.Store.User().AnalyticsUniqueUserCount(""); result.Err != nil { - return nil, model.NewLocAppError("addLicense", "api.license.add_license.invalid_count.app_error", nil, result.Err.Error()) + return nil, model.NewAppError("addLicense", "api.license.add_license.invalid_count.app_error", nil, result.Err.Error(), http.StatusBadRequest) } else { uniqueUserCount := result.Data.(int64) if uniqueUserCount > int64(*license.Features.Users) { - return nil, model.NewLocAppError("addLicense", "api.license.add_license.unique_users.app_error", map[string]interface{}{"Users": *license.Features.Users, "Count": uniqueUserCount}, "") + return nil, model.NewAppError("addLicense", "api.license.add_license.unique_users.app_error", map[string]interface{}{"Users": *license.Features.Users, "Count": uniqueUserCount}, "", http.StatusBadRequest) } } if ok := utils.SetLicense(license); !ok { - return nil, model.NewLocAppError("addLicense", model.EXPIRED_LICENSE_ERROR, nil, "") + return nil, model.NewAppError("addLicense", model.EXPIRED_LICENSE_ERROR, nil, "", http.StatusBadRequest) } record := &model.LicenseRecord{} @@ -69,7 +70,7 @@ func SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) { if result := <-rchan; result.Err != nil { RemoveLicense() - return nil, model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error()) + return nil, model.NewAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error(), http.StatusInternalServerError) } sysVar := &model.System{} @@ -79,10 +80,10 @@ func SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) { if result := <-schan; result.Err != nil { RemoveLicense() - return nil, model.NewLocAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "") + return nil, model.NewAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "", http.StatusInternalServerError) } } else { - return nil, model.NewLocAppError("addLicense", model.INVALID_LICENSE_ERROR, nil, "") + return nil, model.NewAppError("addLicense", model.INVALID_LICENSE_ERROR, nil, "", http.StatusBadRequest) } ReloadConfig() diff --git a/model/client4.go b/model/client4.go index c692a8415..cfecd7b25 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1835,7 +1835,7 @@ func (c *Client4) InvalidateCaches() (bool, *Response) { } } -// UpdateConfig will update the server configuration +// UpdateConfig will update the server configuration. func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) { if r, err := c.DoApiPut(c.GetConfigRoute(), config.ToJson()); err != nil { return nil, &Response{StatusCode: r.StatusCode, Error: err} @@ -1845,6 +1845,50 @@ func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) { } } +// UploadLicenseFile will add a license file to the system. +func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("license", "test-license.mattermost-license"); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetLicenseRoute(), bytes.NewReader(body.Bytes())) + rq.Header.Set("Content-Type", writer.FormDataContentType()) + 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 { + return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetLicenseRoute(), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} + } else if rp.StatusCode >= 300 { + return false, &Response{StatusCode: rp.StatusCode, Error: AppErrorFromJson(rp.Body)} + } else { + defer closeBody(rp) + return CheckStatusOK(rp), BuildResponse(rp) + } +} + +// RemoveLicenseFile will remove the server license it exists. Note that this will +// disable all enterprise features. +func (c *Client4) RemoveLicenseFile() (bool, *Response) { + if r, err := c.DoApiDelete(c.GetLicenseRoute()); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // Webhooks Section // CreateIncomingWebhook creates an incoming webhook for a channel. |