github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+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/gorilla/mux" 11 "github.com/mattermost/mattermost-server/app" 12 "github.com/mattermost/mattermost-server/model" 13 "github.com/mssola/user_agent" 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 err := c.App.SaveConfig(cfg, true) 112 if err != nil { 113 c.Err = err 114 return 115 } 116 117 c.LogAudit("") 118 ReturnStatusOK(w) 119 } 120 121 func recycleDatabaseConnection(c *Context, w http.ResponseWriter, r *http.Request) { 122 c.App.RecycleDatabaseConnection() 123 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 124 ReturnStatusOK(w) 125 } 126 127 func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { 128 cfg := model.ConfigFromJson(r.Body) 129 if cfg == nil { 130 c.SetInvalidParam("testEmail", "config") 131 return 132 } 133 134 err := c.App.TestEmail(c.Session.UserId, cfg) 135 if err != nil { 136 c.Err = err 137 return 138 } 139 140 m := make(map[string]string) 141 m["SUCCESS"] = "true" 142 w.Write([]byte(model.MapToJson(m))) 143 } 144 145 func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) { 146 crs, err := c.App.GetComplianceReports(0, 10000) 147 if err != nil { 148 c.Err = err 149 return 150 } 151 w.Write([]byte(crs.ToJson())) 152 } 153 154 func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { 155 job := model.ComplianceFromJson(r.Body) 156 if job == nil { 157 c.SetInvalidParam("saveComplianceReport", "compliance") 158 return 159 } 160 161 job.UserId = c.Session.UserId 162 163 rjob, err := c.App.SaveComplianceReport(job) 164 if err != nil { 165 c.Err = err 166 return 167 } 168 169 c.LogAudit("") 170 w.Write([]byte(rjob.ToJson())) 171 } 172 173 func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { 174 params := mux.Vars(r) 175 176 id := params["id"] 177 if len(id) != 26 { 178 c.SetInvalidParam("downloadComplianceReport", "id") 179 return 180 } 181 182 job, err := c.App.GetComplianceReport(id) 183 if err != nil { 184 c.Err = err 185 return 186 } 187 188 reportBytes, err := c.App.GetComplianceFile(job) 189 if err != nil { 190 c.Err = err 191 return 192 } 193 194 c.LogAudit("downloaded " + job.Desc) 195 196 w.Header().Set("Cache-Control", "max-age=2592000, public") 197 w.Header().Set("Content-Length", strconv.Itoa(len(reportBytes))) 198 w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer 199 200 // attach extra headers to trigger a download on IE, Edge, and Safari 201 ua := user_agent.New(r.UserAgent()) 202 bname, _ := ua.Browser() 203 204 w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"") 205 206 if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" { 207 // trim off anything before the final / so we just get the file's name 208 w.Header().Set("Content-Type", "application/octet-stream") 209 } 210 211 w.Write(reportBytes) 212 } 213 214 func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { 215 params := mux.Vars(r) 216 teamId := params["id"] 217 name := params["name"] 218 219 rows, err := c.App.GetAnalytics(name, teamId) 220 if err != nil { 221 c.Err = err 222 return 223 } 224 225 if rows == nil { 226 c.SetInvalidParam("getAnalytics", "name") 227 return 228 } 229 230 w.Write([]byte(rows.ToJson())) 231 } 232 233 func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { 234 if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize { 235 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge) 236 return 237 } 238 239 if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil { 240 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.parse.app_error", nil, "", http.StatusBadRequest) 241 return 242 } 243 244 m := r.MultipartForm 245 246 imageArray, ok := m.File["image"] 247 if !ok { 248 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.no_file.app_error", nil, "", http.StatusBadRequest) 249 c.Err.StatusCode = http.StatusBadRequest 250 return 251 } 252 253 if len(imageArray) <= 0 { 254 c.Err = model.NewAppError("uploadBrandImage", "api.admin.upload_brand_image.array.app_error", nil, "", http.StatusBadRequest) 255 c.Err.StatusCode = http.StatusBadRequest 256 return 257 } 258 259 if err := c.App.SaveBrandImage(imageArray[0]); err != nil { 260 c.Err = err 261 return 262 } 263 264 c.LogAudit("") 265 266 ReturnStatusOK(w) 267 } 268 269 func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { 270 if img, err := c.App.GetBrandImage(); err != nil { 271 w.Write(nil) 272 } else { 273 w.Header().Set("Content-Type", "image/png") 274 w.Write(img) 275 } 276 } 277 278 func adminResetMfa(c *Context, w http.ResponseWriter, r *http.Request) { 279 props := model.MapFromJson(r.Body) 280 281 userId := props["user_id"] 282 if len(userId) != 26 { 283 c.SetInvalidParam("adminResetMfa", "user_id") 284 return 285 } 286 287 if err := c.App.DeactivateMfa(userId); err != nil { 288 c.Err = err 289 return 290 } 291 292 c.LogAudit("") 293 294 rdata := map[string]string{} 295 rdata["status"] = "ok" 296 w.Write([]byte(model.MapToJson(rdata))) 297 } 298 299 func adminResetPassword(c *Context, w http.ResponseWriter, r *http.Request) { 300 props := model.MapFromJson(r.Body) 301 302 userId := props["user_id"] 303 if len(userId) != 26 { 304 c.SetInvalidParam("adminResetPassword", "user_id") 305 return 306 } 307 308 newPassword := props["new_password"] 309 if err := c.App.IsPasswordValid(newPassword); err != nil { 310 c.Err = err 311 return 312 } 313 314 if err := c.App.UpdatePasswordByUserIdSendEmail(userId, newPassword, c.T("api.user.reset_password.method")); err != nil { 315 c.Err = err 316 return 317 } 318 319 c.LogAudit("") 320 321 rdata := map[string]string{} 322 rdata["status"] = "ok" 323 w.Write([]byte(model.MapToJson(rdata))) 324 } 325 326 func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) { 327 c.App.SyncLdap() 328 329 rdata := map[string]string{} 330 rdata["status"] = "ok" 331 w.Write([]byte(model.MapToJson(rdata))) 332 } 333 334 func ldapTest(c *Context, w http.ResponseWriter, r *http.Request) { 335 if err := c.App.TestLdap(); err != nil { 336 c.Err = err 337 return 338 } 339 340 rdata := map[string]string{} 341 rdata["status"] = "ok" 342 w.Write([]byte(model.MapToJson(rdata))) 343 } 344 345 func samlMetadata(c *Context, w http.ResponseWriter, r *http.Request) { 346 if result, err := c.App.GetSamlMetadata(); err != nil { 347 c.Err = model.NewAppError("loginWithSaml", "api.admin.saml.metadata.app_error", nil, "err="+err.Message, http.StatusInternalServerError) 348 return 349 } else { 350 w.Header().Set("Content-Type", "application/xml") 351 w.Header().Set("Content-Disposition", "attachment; filename=\"metadata.xml\"") 352 w.Write([]byte(result)) 353 } 354 } 355 356 func addCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 357 err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize) 358 if err != nil { 359 http.Error(w, err.Error(), http.StatusInternalServerError) 360 return 361 } 362 363 m := r.MultipartForm 364 365 fileArray, ok := m.File["certificate"] 366 if !ok { 367 c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.no_file.app_error", nil, "", http.StatusBadRequest) 368 c.Err.StatusCode = http.StatusBadRequest 369 return 370 } 371 372 if len(fileArray) <= 0 { 373 c.Err = model.NewAppError("addCertificate", "api.admin.add_certificate.array.app_error", nil, "", http.StatusBadRequest) 374 c.Err.StatusCode = http.StatusBadRequest 375 return 376 } 377 378 fileData := fileArray[0] 379 380 if err := app.WriteSamlFile(fileData); err != nil { 381 c.Err = err 382 return 383 } 384 ReturnStatusOK(w) 385 } 386 387 func removeCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 388 props := model.MapFromJson(r.Body) 389 390 if err := app.RemoveSamlFile(props["filename"]); err != nil { 391 c.Err = err 392 return 393 } 394 395 ReturnStatusOK(w) 396 } 397 398 func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) { 399 status := c.App.GetSamlCertificateStatus() 400 401 statusMap := map[string]interface{}{} 402 statusMap["IdpCertificateFile"] = status.IdpCertificateFile 403 statusMap["PrivateKeyFile"] = status.PrivateKeyFile 404 statusMap["PublicCertificateFile"] = status.PublicCertificateFile 405 406 w.Write([]byte(model.StringInterfaceToJson(statusMap))) 407 } 408 409 func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) { 410 if profiles, err := c.App.GetRecentlyActiveUsersForTeam(c.TeamId); err != nil { 411 c.Err = err 412 return 413 } else { 414 for _, p := range profiles { 415 sanitizeProfile(c, p) 416 } 417 418 w.Write([]byte(model.UserMapToJson(profiles))) 419 } 420 }