github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/api/admin.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api 5 6 import ( 7 "net/http" 8 "strconv" 9 10 "github.com/avct/uasurfer" 11 "github.com/gorilla/mux" 12 "github.com/mattermost/mattermost-server/app" 13 "github.com/mattermost/mattermost-server/model" 14 ) 15 16 func (api *API) InitAdmin() { 17 api.BaseRoutes.Admin.Handle("/logs", api.ApiAdminSystemRequired(getLogs)).Methods("GET") 18 api.BaseRoutes.Admin.Handle("/audits", api.ApiAdminSystemRequired(getAllAudits)).Methods("GET") 19 api.BaseRoutes.Admin.Handle("/config", api.ApiAdminSystemRequired(getConfig)).Methods("GET") 20 api.BaseRoutes.Admin.Handle("/save_config", api.ApiAdminSystemRequired(saveConfig)).Methods("POST") 21 api.BaseRoutes.Admin.Handle("/reload_config", api.ApiAdminSystemRequired(reloadConfig)).Methods("GET") 22 api.BaseRoutes.Admin.Handle("/invalidate_all_caches", api.ApiAdminSystemRequired(invalidateAllCaches)).Methods("GET") 23 api.BaseRoutes.Admin.Handle("/test_email", api.ApiAdminSystemRequired(testEmail)).Methods("POST") 24 api.BaseRoutes.Admin.Handle("/recycle_db_conn", api.ApiAdminSystemRequired(recycleDatabaseConnection)).Methods("GET") 25 api.BaseRoutes.Admin.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", api.ApiAdminSystemRequired(getAnalytics)).Methods("GET") 26 api.BaseRoutes.Admin.Handle("/analytics/{name:[A-Za-z0-9_]+}", api.ApiAdminSystemRequired(getAnalytics)).Methods("GET") 27 api.BaseRoutes.Admin.Handle("/save_compliance_report", api.ApiAdminSystemRequired(saveComplianceReport)).Methods("POST") 28 api.BaseRoutes.Admin.Handle("/compliance_reports", api.ApiAdminSystemRequired(getComplianceReports)).Methods("GET") 29 api.BaseRoutes.Admin.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", api.ApiAdminSystemRequiredTrustRequester(downloadComplianceReport)).Methods("GET") 30 api.BaseRoutes.Admin.Handle("/upload_brand_image", api.ApiAdminSystemRequired(uploadBrandImage)).Methods("POST") 31 api.BaseRoutes.Admin.Handle("/get_brand_image", api.ApiAppHandlerTrustRequester(getBrandImage)).Methods("GET") 32 api.BaseRoutes.Admin.Handle("/reset_mfa", api.ApiAdminSystemRequired(adminResetMfa)).Methods("POST") 33 api.BaseRoutes.Admin.Handle("/reset_password", api.ApiAdminSystemRequired(adminResetPassword)).Methods("POST") 34 api.BaseRoutes.Admin.Handle("/ldap_sync_now", api.ApiAdminSystemRequired(ldapSyncNow)).Methods("POST") 35 api.BaseRoutes.Admin.Handle("/ldap_test", api.ApiAdminSystemRequired(ldapTest)).Methods("POST") 36 api.BaseRoutes.Admin.Handle("/saml_metadata", api.ApiAppHandler(samlMetadata)).Methods("GET") 37 api.BaseRoutes.Admin.Handle("/add_certificate", api.ApiAdminSystemRequired(addCertificate)).Methods("POST") 38 api.BaseRoutes.Admin.Handle("/remove_certificate", api.ApiAdminSystemRequired(removeCertificate)).Methods("POST") 39 api.BaseRoutes.Admin.Handle("/saml_cert_status", api.ApiAdminSystemRequired(samlCertificateStatus)).Methods("GET") 40 api.BaseRoutes.Admin.Handle("/cluster_status", api.ApiAdminSystemRequired(getClusterStatus)).Methods("GET") 41 api.BaseRoutes.Admin.Handle("/recently_active_users/{team_id:[A-Za-z0-9]+}", api.ApiUserRequired(getRecentlyActiveUsers)).Methods("GET") 42 } 43 44 func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { 45 lines, err := c.App.GetLogs(0, 10000) 46 if err != nil { 47 c.Err = err 48 return 49 } 50 51 w.Write([]byte(model.ArrayToJson(lines))) 52 } 53 54 func getClusterStatus(c *Context, w http.ResponseWriter, r *http.Request) { 55 infos := c.App.GetClusterStatus() 56 57 if c.App.Cluster != nil { 58 w.Header().Set(model.HEADER_CLUSTER_ID, c.App.Cluster.GetClusterId()) 59 } 60 61 w.Write([]byte(model.ClusterInfosToJson(infos))) 62 } 63 64 func getAllAudits(c *Context, w http.ResponseWriter, r *http.Request) { 65 if audits, err := c.App.GetAudits("", 200); err != nil { 66 c.Err = err 67 return 68 } else if c.HandleEtag(audits.Etag(), "Get All Audits", w, r) { 69 return 70 } else { 71 etag := audits.Etag() 72 if len(etag) > 0 { 73 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 74 } 75 76 w.Write([]byte(audits.ToJson())) 77 return 78 } 79 } 80 81 func getConfig(c *Context, w http.ResponseWriter, r *http.Request) { 82 cfg := c.App.GetConfig() 83 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 84 w.Write([]byte(cfg.ToJson())) 85 } 86 87 func reloadConfig(c *Context, w http.ResponseWriter, r *http.Request) { 88 c.App.ReloadConfig() 89 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 90 ReturnStatusOK(w) 91 } 92 93 func invalidateAllCaches(c *Context, w http.ResponseWriter, r *http.Request) { 94 err := c.App.InvalidateAllCaches() 95 if err != nil { 96 c.Err = err 97 return 98 } 99 100 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 101 ReturnStatusOK(w) 102 } 103 104 func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { 105 cfg := model.ConfigFromJson(r.Body) 106 if cfg == nil { 107 c.SetInvalidParam("saveConfig", "config") 108 return 109 } 110 111 // Do not allow plugin uploads to be toggled through the API 112 cfg.PluginSettings.EnableUploads = c.App.GetConfig().PluginSettings.EnableUploads 113 114 err := c.App.SaveConfig(cfg, true) 115 if err != nil { 116 c.Err = err 117 return 118 } 119 120 c.LogAudit("") 121 ReturnStatusOK(w) 122 } 123 124 func recycleDatabaseConnection(c *Context, w http.ResponseWriter, r *http.Request) { 125 c.App.RecycleDatabaseConnection() 126 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 127 ReturnStatusOK(w) 128 } 129 130 func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { 131 cfg := model.ConfigFromJson(r.Body) 132 if cfg == nil { 133 c.SetInvalidParam("testEmail", "config") 134 return 135 } 136 137 err := c.App.TestEmail(c.Session.UserId, cfg) 138 if err != nil { 139 c.Err = err 140 return 141 } 142 143 m := make(map[string]string) 144 m["SUCCESS"] = "true" 145 w.Write([]byte(model.MapToJson(m))) 146 } 147 148 func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) { 149 crs, err := c.App.GetComplianceReports(0, 10000) 150 if err != nil { 151 c.Err = err 152 return 153 } 154 w.Write([]byte(crs.ToJson())) 155 } 156 157 func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { 158 job := model.ComplianceFromJson(r.Body) 159 if job == nil { 160 c.SetInvalidParam("saveComplianceReport", "compliance") 161 return 162 } 163 164 job.UserId = c.Session.UserId 165 166 rjob, err := c.App.SaveComplianceReport(job) 167 if err != nil { 168 c.Err = err 169 return 170 } 171 172 c.LogAudit("") 173 w.Write([]byte(rjob.ToJson())) 174 } 175 176 func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { 177 params := mux.Vars(r) 178 179 id := params["id"] 180 if len(id) != 26 { 181 c.SetInvalidParam("downloadComplianceReport", "id") 182 return 183 } 184 185 job, err := c.App.GetComplianceReport(id) 186 if err != nil { 187 c.Err = err 188 return 189 } 190 191 reportBytes, err := c.App.GetComplianceFile(job) 192 if err != nil { 193 c.Err = err 194 return 195 } 196 197 c.LogAudit("downloaded " + job.Desc) 198 199 w.Header().Set("Cache-Control", "max-age=2592000, public") 200 w.Header().Set("Content-Length", strconv.Itoa(len(reportBytes))) 201 w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer 202 203 // attach extra headers to trigger a download on IE, Edge, and Safari 204 ua := uasurfer.Parse(r.UserAgent()) 205 206 w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"") 207 208 if ua.Browser.Name == uasurfer.BrowserIE || ua.Browser.Name == uasurfer.BrowserSafari { 209 // trim off anything before the final / so we just get the file's name 210 w.Header().Set("Content-Type", "application/octet-stream") 211 } 212 213 w.Write(reportBytes) 214 } 215 216 func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { 217 params := mux.Vars(r) 218 teamId := params["id"] 219 name := params["name"] 220 221 rows, err := c.App.GetAnalytics(name, teamId) 222 if err != nil { 223 c.Err = err 224 return 225 } 226 227 if rows == nil { 228 c.SetInvalidParam("getAnalytics", "name") 229 return 230 } 231 232 w.Write([]byte(rows.ToJson())) 233 } 234 235 func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { 236 if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize { 237 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge) 238 return 239 } 240 241 if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil { 242 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.parse.app_error", nil, "", http.StatusBadRequest) 243 return 244 } 245 246 m := r.MultipartForm 247 248 imageArray, ok := m.File["image"] 249 if !ok { 250 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.no_file.app_error", nil, "", http.StatusBadRequest) 251 c.Err.StatusCode = http.StatusBadRequest 252 return 253 } 254 255 if len(imageArray) <= 0 { 256 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.array.app_error", nil, "", http.StatusBadRequest) 257 c.Err.StatusCode = http.StatusBadRequest 258 return 259 } 260 261 if err := c.App.SaveBrandImage(imageArray[0]); err != nil { 262 c.Err = err 263 return 264 } 265 266 c.LogAudit("") 267 268 ReturnStatusOK(w) 269 } 270 271 func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { 272 if img, err := c.App.GetBrandImage(); err != nil { 273 w.Write(nil) 274 } else { 275 w.Header().Set("Content-Type", "image/png") 276 w.Write(img) 277 } 278 } 279 280 func adminResetMfa(c *Context, w http.ResponseWriter, r *http.Request) { 281 props := model.MapFromJson(r.Body) 282 283 userId := props["user_id"] 284 if len(userId) != 26 { 285 c.SetInvalidParam("adminResetMfa", "user_id") 286 return 287 } 288 289 if err := c.App.DeactivateMfa(userId); err != nil { 290 c.Err = err 291 return 292 } 293 294 c.LogAudit("") 295 296 rdata := map[string]string{} 297 rdata["status"] = "ok" 298 w.Write([]byte(model.MapToJson(rdata))) 299 } 300 301 func adminResetPassword(c *Context, w http.ResponseWriter, r *http.Request) { 302 props := model.MapFromJson(r.Body) 303 304 userId := props["user_id"] 305 if len(userId) != 26 { 306 c.SetInvalidParam("adminResetPassword", "user_id") 307 return 308 } 309 310 newPassword := props["new_password"] 311 if err := c.App.IsPasswordValid(newPassword); err != nil { 312 c.Err = err 313 return 314 } 315 316 if err := c.App.UpdatePasswordByUserIdSendEmail(userId, newPassword, c.T("api.user.reset_password.method")); err != nil { 317 c.Err = err 318 return 319 } 320 321 c.LogAudit("") 322 323 rdata := map[string]string{} 324 rdata["status"] = "ok" 325 w.Write([]byte(model.MapToJson(rdata))) 326 } 327 328 func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) { 329 c.App.SyncLdap() 330 331 rdata := map[string]string{} 332 rdata["status"] = "ok" 333 w.Write([]byte(model.MapToJson(rdata))) 334 } 335 336 func ldapTest(c *Context, w http.ResponseWriter, r *http.Request) { 337 if err := c.App.TestLdap(); err != nil { 338 c.Err = err 339 return 340 } 341 342 rdata := map[string]string{} 343 rdata["status"] = "ok" 344 w.Write([]byte(model.MapToJson(rdata))) 345 } 346 347 func samlMetadata(c *Context, w http.ResponseWriter, r *http.Request) { 348 if result, err := c.App.GetSamlMetadata(); err != nil { 349 c.Err = model.NewAppError("loginWithSaml", "api.admin.saml.metadata.app_error", nil, "err="+err.Message, http.StatusInternalServerError) 350 return 351 } else { 352 w.Header().Set("Content-Type", "application/xml") 353 w.Header().Set("Content-Disposition", "attachment; filename=\"metadata.xml\"") 354 w.Write([]byte(result)) 355 } 356 } 357 358 func addCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 359 err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize) 360 if err != nil { 361 http.Error(w, err.Error(), http.StatusInternalServerError) 362 return 363 } 364 365 m := r.MultipartForm 366 367 fileArray, ok := m.File["certificate"] 368 if !ok { 369 c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.no_file.app_error", nil, "", http.StatusBadRequest) 370 c.Err.StatusCode = http.StatusBadRequest 371 return 372 } 373 374 if len(fileArray) <= 0 { 375 c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.array.app_error", nil, "", http.StatusBadRequest) 376 c.Err.StatusCode = http.StatusBadRequest 377 return 378 } 379 380 fileData := fileArray[0] 381 382 if err := app.WriteSamlFile(fileData); err != nil { 383 c.Err = err 384 return 385 } 386 ReturnStatusOK(w) 387 } 388 389 func removeCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 390 props := model.MapFromJson(r.Body) 391 392 if err := app.RemoveSamlFile(props["filename"]); err != nil { 393 c.Err = err 394 return 395 } 396 397 ReturnStatusOK(w) 398 } 399 400 func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) { 401 status := c.App.GetSamlCertificateStatus() 402 403 statusMap := map[string]interface{}{} 404 statusMap["IdpCertificateFile"] = status.IdpCertificateFile 405 statusMap["PrivateKeyFile"] = status.PrivateKeyFile 406 statusMap["PublicCertificateFile"] = status.PublicCertificateFile 407 408 w.Write([]byte(model.StringInterfaceToJson(statusMap))) 409 } 410 411 func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) { 412 if profiles, err := c.App.GetRecentlyActiveUsersForTeam(c.TeamId); err != nil { 413 c.Err = err 414 return 415 } else { 416 for _, p := range profiles { 417 sanitizeProfile(c, p) 418 } 419 420 w.Write([]byte(model.UserMapToJson(profiles))) 421 } 422 }