github.com/mad-app/mattermost-server@v5.11.1+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 "encoding/json" 8 "fmt" 9 "net/http" 10 "runtime" 11 12 "github.com/mattermost/mattermost-server/mlog" 13 "github.com/mattermost/mattermost-server/model" 14 "github.com/mattermost/mattermost-server/services/filesstore" 15 "github.com/mattermost/mattermost-server/utils" 16 ) 17 18 const REDIRECT_LOCATION_CACHE_SIZE = 10000 19 20 var redirectLocationDataCache = utils.NewLru(REDIRECT_LOCATION_CACHE_SIZE) 21 22 func (api *API) InitSystem() { 23 api.BaseRoutes.System.Handle("/ping", api.ApiHandler(getSystemPing)).Methods("GET") 24 25 api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET") 26 27 api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET") 28 api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST") 29 api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST") 30 api.BaseRoutes.ApiRoot.Handle("/database/recycle", api.ApiSessionRequired(databaseRecycle)).Methods("POST") 31 api.BaseRoutes.ApiRoot.Handle("/caches/invalidate", api.ApiSessionRequired(invalidateCaches)).Methods("POST") 32 33 api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiSessionRequired(getLogs)).Methods("GET") 34 api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiHandler(postLog)).Methods("POST") 35 36 api.BaseRoutes.ApiRoot.Handle("/analytics/old", api.ApiSessionRequired(getAnalytics)).Methods("GET") 37 38 api.BaseRoutes.ApiRoot.Handle("/redirect_location", api.ApiSessionRequiredTrustRequester(getRedirectLocation)).Methods("GET") 39 } 40 41 func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) { 42 43 actualGoroutines := runtime.NumGoroutine() 44 if *c.App.Config().ServiceSettings.GoroutineHealthThreshold <= 0 || actualGoroutines <= *c.App.Config().ServiceSettings.GoroutineHealthThreshold { 45 m := make(map[string]string) 46 m[model.STATUS] = model.STATUS_OK 47 48 reqs := c.App.Config().ClientRequirements 49 m["AndroidLatestVersion"] = reqs.AndroidLatestVersion 50 m["AndroidMinVersion"] = reqs.AndroidMinVersion 51 m["DesktopLatestVersion"] = reqs.DesktopLatestVersion 52 m["DesktopMinVersion"] = reqs.DesktopMinVersion 53 m["IosLatestVersion"] = reqs.IosLatestVersion 54 m["IosMinVersion"] = reqs.IosMinVersion 55 56 w.Write([]byte(model.MapToJson(m))) 57 } else { 58 rdata := map[string]string{} 59 rdata["status"] = "unhealthy" 60 61 mlog.Warn(fmt.Sprintf("The number of running goroutines is over the health threshold %v of %v", actualGoroutines, *c.App.Config().ServiceSettings.GoroutineHealthThreshold)) 62 63 w.WriteHeader(http.StatusInternalServerError) 64 w.Write([]byte(model.MapToJson(rdata))) 65 } 66 } 67 68 func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { 69 cfg := model.ConfigFromJson(r.Body) 70 if cfg == nil { 71 cfg = c.App.Config() 72 } 73 74 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 75 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 76 return 77 } 78 79 if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin { 80 c.Err = model.NewAppError("testEmail", "api.restricted_system_admin", nil, "", http.StatusForbidden) 81 return 82 } 83 84 err := c.App.TestEmail(c.App.Session.UserId, cfg) 85 if err != nil { 86 c.Err = err 87 return 88 } 89 90 ReturnStatusOK(w) 91 } 92 93 func getAudits(c *Context, w http.ResponseWriter, r *http.Request) { 94 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 95 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 96 return 97 } 98 99 audits, err := c.App.GetAuditsPage("", c.Params.Page, c.Params.PerPage) 100 101 if err != nil { 102 c.Err = err 103 return 104 } 105 106 w.Write([]byte(audits.ToJson())) 107 } 108 109 func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) { 110 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 111 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 112 return 113 } 114 115 if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin { 116 c.Err = model.NewAppError("databaseRecycle", "api.restricted_system_admin", nil, "", http.StatusForbidden) 117 return 118 } 119 120 c.App.RecycleDatabaseConnection() 121 122 ReturnStatusOK(w) 123 } 124 125 func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) { 126 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 127 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 128 return 129 } 130 131 if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin { 132 c.Err = model.NewAppError("invalidateCaches", "api.restricted_system_admin", nil, "", http.StatusForbidden) 133 return 134 } 135 136 err := c.App.InvalidateAllCaches() 137 if err != nil { 138 c.Err = err 139 return 140 } 141 142 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 143 ReturnStatusOK(w) 144 } 145 146 func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { 147 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 148 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 149 return 150 } 151 152 lines, err := c.App.GetLogs(c.Params.Page, c.Params.LogsPerPage) 153 if err != nil { 154 c.Err = err 155 return 156 } 157 158 w.Write([]byte(model.ArrayToJson(lines))) 159 } 160 161 func postLog(c *Context, w http.ResponseWriter, r *http.Request) { 162 forceToDebug := false 163 164 if !*c.App.Config().ServiceSettings.EnableDeveloper { 165 if c.App.Session.UserId == "" { 166 c.Err = model.NewAppError("postLog", "api.context.permissions.app_error", nil, "", http.StatusForbidden) 167 return 168 } 169 170 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 171 forceToDebug = true 172 } 173 } 174 175 m := model.MapFromJson(r.Body) 176 lvl := m["level"] 177 msg := m["message"] 178 179 if len(msg) > 400 { 180 msg = msg[0:399] 181 } 182 183 if !forceToDebug && lvl == "ERROR" { 184 err := &model.AppError{} 185 err.Message = msg 186 err.Id = msg 187 err.Where = "client" 188 c.LogError(err) 189 } else { 190 mlog.Debug(fmt.Sprint(msg)) 191 } 192 193 m["message"] = msg 194 w.Write([]byte(model.MapToJson(m))) 195 } 196 197 func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { 198 name := r.URL.Query().Get("name") 199 teamId := r.URL.Query().Get("team_id") 200 201 if name == "" { 202 name = "standard" 203 } 204 205 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 206 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 207 return 208 } 209 210 rows, err := c.App.GetAnalytics(name, teamId) 211 if err != nil { 212 c.Err = err 213 return 214 } 215 216 if rows == nil { 217 c.SetInvalidParam("name") 218 return 219 } 220 221 w.Write([]byte(rows.ToJson())) 222 } 223 224 func getSupportedTimezones(c *Context, w http.ResponseWriter, r *http.Request) { 225 supportedTimezones := c.App.Timezones.GetSupported() 226 if supportedTimezones == nil { 227 supportedTimezones = make([]string, 0) 228 } 229 230 b, err := json.Marshal(supportedTimezones) 231 if err != nil { 232 c.Log.Warn("Unable to marshal JSON in timezones.", mlog.Err(err)) 233 w.WriteHeader(http.StatusInternalServerError) 234 } 235 236 w.Write(b) 237 } 238 239 func testS3(c *Context, w http.ResponseWriter, r *http.Request) { 240 cfg := model.ConfigFromJson(r.Body) 241 if cfg == nil { 242 cfg = c.App.Config() 243 } 244 245 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 246 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 247 return 248 } 249 250 if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin { 251 c.Err = model.NewAppError("testS3", "api.restricted_system_admin", nil, "", http.StatusForbidden) 252 return 253 } 254 255 err := filesstore.CheckMandatoryS3Fields(&cfg.FileSettings) 256 if err != nil { 257 c.Err = err 258 return 259 } 260 261 if *cfg.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING { 262 cfg.FileSettings.AmazonS3SecretAccessKey = c.App.Config().FileSettings.AmazonS3SecretAccessKey 263 } 264 265 license := c.App.License() 266 backend, appErr := filesstore.NewFileBackend(&cfg.FileSettings, license != nil && *license.Features.Compliance) 267 if appErr == nil { 268 appErr = backend.TestConnection() 269 } 270 if appErr != nil { 271 c.Err = appErr 272 return 273 } 274 275 ReturnStatusOK(w) 276 } 277 278 func getRedirectLocation(c *Context, w http.ResponseWriter, r *http.Request) { 279 m := make(map[string]string) 280 m["location"] = "" 281 282 if !*c.App.Config().ServiceSettings.EnableLinkPreviews { 283 w.Write([]byte(model.MapToJson(m))) 284 return 285 } 286 287 url := r.URL.Query().Get("url") 288 if len(url) == 0 { 289 c.SetInvalidParam("url") 290 return 291 } 292 293 if location, ok := redirectLocationDataCache.Get(url); ok { 294 m["location"] = location.(string) 295 w.Write([]byte(model.MapToJson(m))) 296 return 297 } 298 299 client := c.App.HTTPService.MakeClient(false) 300 client.CheckRedirect = func(req *http.Request, via []*http.Request) error { 301 return http.ErrUseLastResponse 302 } 303 304 res, err := client.Head(url) 305 if err != nil { 306 // Cache failures to prevent retries. 307 redirectLocationDataCache.AddWithExpiresInSecs(url, "", 3600) // Expires after 1 hour 308 // Always return a success status and a JSON string to limit information returned to client. 309 w.Write([]byte(model.MapToJson(m))) 310 return 311 } 312 313 location := res.Header.Get("Location") 314 redirectLocationDataCache.AddWithExpiresInSecs(url, location, 3600) // Expires after 1 hour 315 m["location"] = location 316 317 w.Write([]byte(model.MapToJson(m))) 318 return 319 }