github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+incompatible/api4/system.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api4 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "net/http" 11 "runtime" 12 13 "github.com/mattermost/mattermost-server/mlog" 14 "github.com/mattermost/mattermost-server/model" 15 "github.com/mattermost/mattermost-server/services/filesstore" 16 ) 17 18 func (api *API) InitSystem() { 19 api.BaseRoutes.System.Handle("/ping", api.ApiHandler(getSystemPing)).Methods("GET") 20 21 api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET") 22 23 api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(getConfig)).Methods("GET") 24 api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(updateConfig)).Methods("PUT") 25 api.BaseRoutes.ApiRoot.Handle("/config/reload", api.ApiSessionRequired(configReload)).Methods("POST") 26 api.BaseRoutes.ApiRoot.Handle("/config/client", api.ApiHandler(getClientConfig)).Methods("GET") 27 api.BaseRoutes.ApiRoot.Handle("/config/environment", api.ApiSessionRequired(getEnvironmentConfig)).Methods("GET") 28 29 api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(addLicense)).Methods("POST") 30 api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(removeLicense)).Methods("DELETE") 31 api.BaseRoutes.ApiRoot.Handle("/license/client", api.ApiHandler(getClientLicense)).Methods("GET") 32 33 api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET") 34 api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST") 35 api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST") 36 api.BaseRoutes.ApiRoot.Handle("/database/recycle", api.ApiSessionRequired(databaseRecycle)).Methods("POST") 37 api.BaseRoutes.ApiRoot.Handle("/caches/invalidate", api.ApiSessionRequired(invalidateCaches)).Methods("POST") 38 39 api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiSessionRequired(getLogs)).Methods("GET") 40 api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiHandler(postLog)).Methods("POST") 41 42 api.BaseRoutes.ApiRoot.Handle("/analytics/old", api.ApiSessionRequired(getAnalytics)).Methods("GET") 43 44 api.BaseRoutes.ApiRoot.Handle("/redirect_location", api.ApiSessionRequiredTrustRequester(getRedirectLocation)).Methods("GET") 45 } 46 47 func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) { 48 49 actualGoroutines := runtime.NumGoroutine() 50 if *c.App.Config().ServiceSettings.GoroutineHealthThreshold <= 0 || actualGoroutines <= *c.App.Config().ServiceSettings.GoroutineHealthThreshold { 51 m := make(map[string]string) 52 m[model.STATUS] = model.STATUS_OK 53 54 reqs := c.App.Config().ClientRequirements 55 m["AndroidLatestVersion"] = reqs.AndroidLatestVersion 56 m["AndroidMinVersion"] = reqs.AndroidMinVersion 57 m["DesktopLatestVersion"] = reqs.DesktopLatestVersion 58 m["DesktopMinVersion"] = reqs.DesktopMinVersion 59 m["IosLatestVersion"] = reqs.IosLatestVersion 60 m["IosMinVersion"] = reqs.IosMinVersion 61 62 w.Write([]byte(model.MapToJson(m))) 63 } else { 64 rdata := map[string]string{} 65 rdata["status"] = "unhealthy" 66 67 mlog.Warn(fmt.Sprintf("The number of running goroutines is over the health threshold %v of %v", actualGoroutines, *c.App.Config().ServiceSettings.GoroutineHealthThreshold)) 68 69 w.WriteHeader(http.StatusInternalServerError) 70 w.Write([]byte(model.MapToJson(rdata))) 71 } 72 } 73 74 func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { 75 cfg := model.ConfigFromJson(r.Body) 76 if cfg == nil { 77 cfg = c.App.Config() 78 } 79 80 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 81 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 82 return 83 } 84 85 err := c.App.TestEmail(c.Session.UserId, cfg) 86 if err != nil { 87 c.Err = err 88 return 89 } 90 91 ReturnStatusOK(w) 92 } 93 94 func getConfig(c *Context, w http.ResponseWriter, r *http.Request) { 95 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 96 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 97 return 98 } 99 100 cfg := c.App.GetConfig() 101 102 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 103 w.Write([]byte(cfg.ToJson())) 104 } 105 106 func configReload(c *Context, w http.ResponseWriter, r *http.Request) { 107 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 108 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 109 return 110 } 111 112 c.App.ReloadConfig() 113 114 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 115 ReturnStatusOK(w) 116 } 117 118 func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) { 119 cfg := model.ConfigFromJson(r.Body) 120 if cfg == nil { 121 c.SetInvalidParam("config") 122 return 123 } 124 125 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 126 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 127 return 128 } 129 130 // Do not allow plugin uploads to be toggled through the API 131 cfg.PluginSettings.EnableUploads = c.App.GetConfig().PluginSettings.EnableUploads 132 133 err := c.App.SaveConfig(cfg, true) 134 if err != nil { 135 c.Err = err 136 return 137 } 138 139 c.LogAudit("updateConfig") 140 141 cfg = c.App.GetConfig() 142 143 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 144 w.Write([]byte(cfg.ToJson())) 145 } 146 147 func getAudits(c *Context, w http.ResponseWriter, r *http.Request) { 148 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 149 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 150 return 151 } 152 153 audits, err := c.App.GetAuditsPage("", c.Params.Page, c.Params.PerPage) 154 155 if err != nil { 156 c.Err = err 157 return 158 } 159 160 w.Write([]byte(audits.ToJson())) 161 } 162 163 func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) { 164 165 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 166 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 167 return 168 } 169 170 c.App.RecycleDatabaseConnection() 171 172 ReturnStatusOK(w) 173 } 174 175 func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) { 176 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 177 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 178 return 179 } 180 181 err := c.App.InvalidateAllCaches() 182 if err != nil { 183 c.Err = err 184 return 185 } 186 187 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 188 ReturnStatusOK(w) 189 } 190 191 func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { 192 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 193 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 194 return 195 } 196 197 lines, err := c.App.GetLogs(c.Params.Page, c.Params.LogsPerPage) 198 if err != nil { 199 c.Err = err 200 return 201 } 202 203 w.Write([]byte(model.ArrayToJson(lines))) 204 } 205 206 func postLog(c *Context, w http.ResponseWriter, r *http.Request) { 207 forceToDebug := false 208 209 if !*c.App.Config().ServiceSettings.EnableDeveloper { 210 if c.Session.UserId == "" { 211 c.Err = model.NewAppError("postLog", "api.context.permissions.app_error", nil, "", http.StatusForbidden) 212 return 213 } 214 215 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 216 forceToDebug = true 217 } 218 } 219 220 m := model.MapFromJson(r.Body) 221 lvl := m["level"] 222 msg := m["message"] 223 224 if len(msg) > 400 { 225 msg = msg[0:399] 226 } 227 228 if !forceToDebug && lvl == "ERROR" { 229 err := &model.AppError{} 230 err.Message = msg 231 err.Id = msg 232 err.Where = "client" 233 c.LogError(err) 234 } else { 235 mlog.Debug(fmt.Sprint(msg)) 236 } 237 238 m["message"] = msg 239 w.Write([]byte(model.MapToJson(m))) 240 } 241 242 func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) { 243 format := r.URL.Query().Get("format") 244 245 if format == "" { 246 c.Err = model.NewAppError("getClientConfig", "api.config.client.old_format.app_error", nil, "", http.StatusNotImplemented) 247 return 248 } 249 250 if format != "old" { 251 c.SetInvalidParam("format") 252 return 253 } 254 255 var config map[string]string 256 if *c.App.Config().ServiceSettings.ExperimentalLimitClientConfig && len(c.Session.UserId) == 0 { 257 config = c.App.LimitedClientConfigWithComputed() 258 } else { 259 config = c.App.ClientConfigWithComputed() 260 } 261 262 w.Write([]byte(model.MapToJson(config))) 263 } 264 265 func getEnvironmentConfig(c *Context, w http.ResponseWriter, r *http.Request) { 266 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 267 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 268 return 269 } 270 271 envConfig := c.App.GetEnvironmentConfig() 272 273 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 274 w.Write([]byte(model.StringInterfaceToJson(envConfig))) 275 } 276 277 func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) { 278 format := r.URL.Query().Get("format") 279 280 if format == "" { 281 c.Err = model.NewAppError("getClientLicense", "api.license.client.old_format.app_error", nil, "", http.StatusNotImplemented) 282 return 283 } 284 285 if format != "old" { 286 c.SetInvalidParam("format") 287 return 288 } 289 290 etag := c.App.GetClientLicenseEtag(true) 291 if c.HandleEtag(etag, "Get Client License", w, r) { 292 return 293 } 294 295 var clientLicense map[string]string 296 297 if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 298 clientLicense = c.App.ClientLicense() 299 } else { 300 clientLicense = c.App.GetSanitizedClientLicense() 301 } 302 303 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 304 w.Write([]byte(model.MapToJson(clientLicense))) 305 } 306 307 func addLicense(c *Context, w http.ResponseWriter, r *http.Request) { 308 c.LogAudit("attempt") 309 310 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 311 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 312 return 313 } 314 315 err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize) 316 if err != nil { 317 http.Error(w, err.Error(), http.StatusBadRequest) 318 return 319 } 320 321 m := r.MultipartForm 322 323 fileArray, ok := m.File["license"] 324 if !ok { 325 c.Err = model.NewAppError("addLicense", "api.license.add_license.no_file.app_error", nil, "", http.StatusBadRequest) 326 return 327 } 328 329 if len(fileArray) <= 0 { 330 c.Err = model.NewAppError("addLicense", "api.license.add_license.array.app_error", nil, "", http.StatusBadRequest) 331 return 332 } 333 334 fileData := fileArray[0] 335 336 file, err := fileData.Open() 337 if err != nil { 338 c.Err = model.NewAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error(), http.StatusBadRequest) 339 return 340 } 341 defer file.Close() 342 343 buf := bytes.NewBuffer(nil) 344 io.Copy(buf, file) 345 346 license, appErr := c.App.SaveLicense(buf.Bytes()) 347 if appErr != nil { 348 if appErr.Id == model.EXPIRED_LICENSE_ERROR { 349 c.LogAudit("failed - expired or non-started license") 350 } else if appErr.Id == model.INVALID_LICENSE_ERROR { 351 c.LogAudit("failed - invalid license") 352 } else { 353 c.LogAudit("failed - unable to save license") 354 } 355 c.Err = appErr 356 return 357 } 358 359 c.LogAudit("success") 360 w.Write([]byte(license.ToJson())) 361 } 362 363 func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) { 364 c.LogAudit("attempt") 365 366 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 367 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 368 return 369 } 370 371 if err := c.App.RemoveLicense(); err != nil { 372 c.Err = err 373 return 374 } 375 376 c.LogAudit("success") 377 ReturnStatusOK(w) 378 } 379 380 func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { 381 name := r.URL.Query().Get("name") 382 teamId := r.URL.Query().Get("team_id") 383 384 if name == "" { 385 name = "standard" 386 } 387 388 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 389 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 390 return 391 } 392 393 rows, err := c.App.GetAnalytics(name, teamId) 394 if err != nil { 395 c.Err = err 396 return 397 } 398 399 if rows == nil { 400 c.SetInvalidParam("name") 401 return 402 } 403 404 w.Write([]byte(rows.ToJson())) 405 } 406 407 func getSupportedTimezones(c *Context, w http.ResponseWriter, r *http.Request) { 408 supportedTimezones := c.App.Timezones() 409 410 if supportedTimezones != nil { 411 w.Write([]byte(model.TimezonesToJson(supportedTimezones))) 412 return 413 } 414 415 emptyTimezones := make([]string, 0) 416 w.Write([]byte(model.TimezonesToJson(emptyTimezones))) 417 } 418 419 func testS3(c *Context, w http.ResponseWriter, r *http.Request) { 420 cfg := model.ConfigFromJson(r.Body) 421 if cfg == nil { 422 cfg = c.App.Config() 423 } 424 425 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 426 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 427 return 428 } 429 430 err := filesstore.CheckMandatoryS3Fields(&cfg.FileSettings) 431 if err != nil { 432 c.Err = err 433 return 434 } 435 436 if cfg.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING { 437 cfg.FileSettings.AmazonS3SecretAccessKey = c.App.Config().FileSettings.AmazonS3SecretAccessKey 438 } 439 440 license := c.App.License() 441 backend, appErr := filesstore.NewFileBackend(&cfg.FileSettings, license != nil && *license.Features.Compliance) 442 if appErr == nil { 443 appErr = backend.TestConnection() 444 } 445 if appErr != nil { 446 c.Err = appErr 447 return 448 } 449 450 ReturnStatusOK(w) 451 } 452 453 func getRedirectLocation(c *Context, w http.ResponseWriter, r *http.Request) { 454 m := make(map[string]string) 455 m["location"] = "" 456 cfg := c.App.GetConfig() 457 if !*cfg.ServiceSettings.EnableLinkPreviews { 458 w.Write([]byte(model.MapToJson(m))) 459 return 460 } 461 url := r.URL.Query().Get("url") 462 if len(url) == 0 { 463 c.SetInvalidParam("url") 464 return 465 } 466 467 client := &http.Client{ 468 CheckRedirect: func(req *http.Request, via []*http.Request) error { 469 return http.ErrUseLastResponse 470 }, 471 } 472 473 res, err := client.Head(url) 474 if err != nil { 475 // Always return a success status and a JSON string to limit the amount of information returned to a 476 // hacker attempting to use Mattermost to probe a private network. 477 w.Write([]byte(model.MapToJson(m))) 478 return 479 } 480 481 m["location"] = res.Header.Get("Location") 482 483 w.Write([]byte(model.MapToJson(m))) 484 return 485 }