mattermost/api4/system.go
Martin Kraft 8354206e5c
MM-25543: New Admin Roles (#14960)
* MM-23832: Initial set of changes

* MM-23832: further iteration

* MM-23832: further iteration

* MM-23832: further iteration

* MM-23832: Fixes merge.

* create migration for new Roles

* MM-23832: Renames some roles.

* MM-23832: Adds ability to see logs.

* MM-23832: Removes manage roles from restricted admin.

* MM-23832: Make authentication section read-only for restricted admin.

* MM-23832: Allow restricted admin to purge caches.

* MM-23832: Adds ability to recycle DB connections.

* MM-23832: Adds ability to purge indexes.

* MM-23832: Adds ability to test email and S3 config.

* MM-23832: Adds abilituy to read job status.

* MM-23832: Adds ability to read plugin statuses.

* MM-23832: Renames Restricted Admin to System Manager.

* MM-23832: Adds manage team roles to system_user_manager.

* MM-23832: Updates some permissions.

* MM-23832: Allow get all channels and get moderations.

* MM-23832: Adds some permissions to User Manager.

* MM-23832: Remove write users from user manager.

* MM-23832: Changes permissions for the usermanagement > users sysconsole section.

* MM-23832: Removes read_settings and write_settings permissions. Ensures the usermanagement parent permissions encompass the sub-permissions.

* MM-23832: Updates permissions.

* MM-23832: Changes some permissions checks, adds new permissions to roles.

* MM-23832: Adds ability to update a role.

* MM-23832: Permissions updates.

* MM-23832: Removes write access to plugins for system manager.

* MM-23832: Removes read compliance from new roles.

* MM-23832: Adds mock for new roles creation migration.

* MM-23832: Changes to variadic param.

* MM-23832: Removes some duplication in the permissions model. Renames some permissions constants.

* MM-23832: Updates some migrations.

* MM-23832: Removes some unnecessary constants.

* MM-23832: Changes back to old app method name.

* MM-23832: Fixes incorrect permission check.

* MM-23832: Changes write to read permission check.

* MM-23832: Removes the authentication permission from link/unlink group.

* MM-23832: Enable testing LDAP with read permissions.

* MM-23832: Make testing elasticsearch a read permission.

* MM-23832: Warn metrics are associated to any system console read permissions.

* MM-23832: Updates some permissions checks.

* MM-23832: Removes non-systemconsole permissions from roles.

* MM-23832: Update default permission assignment of sysadmin.

* MM-23832: Fixes incorrect permission check. Removes some unused stuff.

* MM-23832: Update permission to check.

* MM-23832: Switches to struct tags.

* MM-23832: Adds some docs for the permissions tag.

* MM-23832: Removes whitespace.

* MM-23832: Combines system admin restricted access with other acess-control tag.

* MM-23832: Fixes some tests.

* MM-23832: Clarifies docs, does not assume prior permission check in '-' access value case.

* MM-23832: Updates to correct access tag value.

* MM-23832: Adds test of the config settings tag access.

* MM-23832: Undoes whitespace change.

* MM-23832: Removes comment.

* MM-23832: Adds the permissions to the new roles rather than using OR conditions on the permissions checks.

* MM-23832: Removes or condition on permission check.

* MM-23832: Updates mapping.

* MM-23832: Typo fix.

* MM-23832: Adds new 'read_jobs' permission.

* MM-23832: Add read_jobs to all roles with manage_jobs.

* MM-23832: Adds new permission read_other_users_teams.

* MM-23832: Adds read filtering of config.

* MM-23932: Change tag value.

* MM-23832: Fixes some tests. Adds test for read config access tag.

* MM-23832: Adds permissions to list teams.

* MM-23832: Removes the '-' tag value. Adds a new permission read_channel_groups. Updates a permission check.

* MM-23832: Removes unnecessary parent permission for user_management. Fixes permission check change error.

* MM-23832: Removes unused parameter to filter/merge function.

* MM-23832: Renames migration name.

* MM-23832: Fix for godoc.

* MM-23832: Fixes tests.

* MM-23832: Only makes a map once rather than every function call. Doesn't require access tag on config field structs. Reverts one test update and fixes another.

* MM-23832: Removes all of the unnecessary uses of (*App).SessionHasPermissionToAny since removing the user_management parent permission.

* MM-23832: Updates constant type.

* MM-23832: Removes unnecessary comment.

* MM-23832: Renames permissions.

* MM-23832: Fix for permission name changes.

* MM-23832: Adds missing config access tags. Adds some requirec ancillary permissions for write_usermanagement_teams.

* MM-23832: Adds local API endpoint for getting config.

* MM-23832: If tag value is blank or restrict_sys_admin_write then don't do the permission check.

* MM-23832: nil check for strings prior to dereferencing.

* MM-23832: Fix for config display logic.

* MM-23832: Updates godoc.

* MM-23832: Delays the unrestricted check for parity with other permissions checks if the channel id does not exist.

* MM-23832: Removes tautology.

* MM-23832: Re-adds status code check.

* MM-23832: Adds new permission to edit brand image.

* MM-23832: Exports variable for use by mmctl.

* MM-23832: Initialize exported map for use by mmctl.

* MM-23832: Accept deprecated permissions as valid.

* MM-23832: Adds missing permissions to archive a channel.

* MM-23832: Adds missing permissions for managing team.

* MM-23832: Properly filters config values in patch and update API responses.

* MM-23832: Fixes license viewing and writing permissions.

* MM-23832: Require license to assign 'new system roles'.

* MM-23832: Adds translation keys.

* MM-23832: Updates translation order.

* MM-27529: Splits read_channel_groups into read_public_channel_groups and read_private_channel_groups.

* MM-23832: Prevent read-only permissions from editing site url test parameter.

* MM-23832: Prevent read permissions from sniffing ports and elastic password.

* MM-23832: Adds missing permission required for write user management channels.

* MM-23832: Allows new roles to search for channels.

* MM-23832: Adds ability for system_manager to manage jobs.

* MM-23832: Cluster status access by sysconsole permission, not manage_system.

* MM-23832: Adds 'add_user_to_team' permission to sysconsole write usermanagement teams.

* MM-23832: Fixes lint.

* MM-23832: Test fix.

* MM-23832: Test fix.

Co-authored-by: Catalin Tomai <catalin.tomai@mattermost.com>
Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2020-08-21 16:49:31 -04:00

724 lines
22 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"runtime"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/cache"
"github.com/mattermost/mattermost-server/v5/services/filesstore"
"github.com/mattermost/mattermost-server/v5/services/upgrader"
)
const (
REDIRECT_LOCATION_CACHE_SIZE = 10000
DEFAULT_SERVER_BUSY_SECONDS = 3600
MAX_SERVER_BUSY_SECONDS = 86400
)
var redirectLocationDataCache = cache.NewLRU(&cache.LRUOptions{
Size: REDIRECT_LOCATION_CACHE_SIZE,
})
func (api *API) InitSystem() {
api.BaseRoutes.System.Handle("/ping", api.ApiHandler(getSystemPing)).Methods("GET")
api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/site_url/test", api.ApiSessionRequired(testSiteURL)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/database/recycle", api.ApiSessionRequired(databaseRecycle)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/caches/invalidate", api.ApiSessionRequired(invalidateCaches)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiSessionRequired(getLogs)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiHandler(postLog)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/analytics/old", api.ApiSessionRequired(getAnalytics)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/redirect_location", api.ApiSessionRequiredTrustRequester(getRedirectLocation)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/notifications/ack", api.ApiSessionRequired(pushNotificationAck)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(setServerBusy)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(getServerBusyExpires)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(clearServerBusy)).Methods("DELETE")
api.BaseRoutes.ApiRoot.Handle("/upgrade_to_enterprise", api.ApiSessionRequired(upgradeToEnterprise)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/upgrade_to_enterprise/status", api.ApiSessionRequired(upgradeToEnterpriseStatus)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/restart", api.ApiSessionRequired(restart)).Methods("POST")
api.BaseRoutes.ApiRoot.Handle("/warn_metrics/status", api.ApiSessionRequired(getWarnMetricsStatus)).Methods("GET")
api.BaseRoutes.ApiRoot.Handle("/warn_metrics/ack/{warn_metric_id:[A-Za-z0-9-_]+}", api.ApiHandler(sendWarnMetricAckEmail)).Methods("POST")
}
func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
reqs := c.App.Config().ClientRequirements
s := make(map[string]string)
s[model.STATUS] = model.STATUS_OK
s["AndroidLatestVersion"] = reqs.AndroidLatestVersion
s["AndroidMinVersion"] = reqs.AndroidMinVersion
s["DesktopLatestVersion"] = reqs.DesktopLatestVersion
s["DesktopMinVersion"] = reqs.DesktopMinVersion
s["IosLatestVersion"] = reqs.IosLatestVersion
s["IosMinVersion"] = reqs.IosMinVersion
actualGoroutines := runtime.NumGoroutine()
if *c.App.Config().ServiceSettings.GoroutineHealthThreshold > 0 && actualGoroutines >= *c.App.Config().ServiceSettings.GoroutineHealthThreshold {
mlog.Warn("The number of running goroutines is over the health threshold", mlog.Int("goroutines", actualGoroutines), mlog.Int("health_threshold", *c.App.Config().ServiceSettings.GoroutineHealthThreshold))
s[model.STATUS] = model.STATUS_UNHEALTHY
}
// Enhanced ping health check:
// If an extra form value is provided then perform extra health checks for
// database and file storage backends.
if r.FormValue("get_server_status") != "" {
dbStatusKey := "database_status"
s[dbStatusKey] = model.STATUS_OK
// Database Write Check
currentTime := fmt.Sprintf("%d", time.Now().Unix())
healthCheckKey := fmt.Sprintf("health_check_%s", c.App.GetClusterId())
writeErr := c.App.Srv().Store.System().SaveOrUpdate(&model.System{
Name: healthCheckKey,
Value: currentTime,
})
if writeErr != nil {
mlog.Warn("Unable to write to database.", mlog.Err(writeErr))
s[dbStatusKey] = model.STATUS_UNHEALTHY
s[model.STATUS] = model.STATUS_UNHEALTHY
}
_, writeErr = c.App.Srv().Store.System().PermanentDeleteByName(healthCheckKey)
if writeErr != nil {
mlog.Warn("Unable to remove ping health check value from database.", mlog.Err(writeErr))
s[dbStatusKey] = model.STATUS_UNHEALTHY
s[model.STATUS] = model.STATUS_UNHEALTHY
}
if s[dbStatusKey] == model.STATUS_OK {
mlog.Debug("Able to write to database.")
}
filestoreStatusKey := "filestore_status"
s[filestoreStatusKey] = model.STATUS_OK
license := c.App.Srv().License()
backend, appErr := filesstore.NewFileBackend(&c.App.Config().FileSettings, license != nil && *license.Features.Compliance)
if appErr == nil {
appErr = backend.TestConnection()
if appErr != nil {
s[filestoreStatusKey] = model.STATUS_UNHEALTHY
s[model.STATUS] = model.STATUS_UNHEALTHY
}
} else {
mlog.Debug("Unable to get filestore for ping status.", mlog.Err(appErr))
s[filestoreStatusKey] = model.STATUS_UNHEALTHY
s[model.STATUS] = model.STATUS_UNHEALTHY
}
w.Header().Set(model.STATUS, s[model.STATUS])
w.Header().Set(dbStatusKey, s[dbStatusKey])
w.Header().Set(filestoreStatusKey, s[filestoreStatusKey])
}
if s[model.STATUS] != model.STATUS_OK {
w.WriteHeader(http.StatusInternalServerError)
}
w.Write([]byte(model.MapToJson(s)))
}
func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
cfg := model.ConfigFromJson(r.Body)
if cfg == nil {
cfg = c.App.Config()
}
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT)
return
}
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("testEmail", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
}
err := c.App.TestEmail(c.App.Session().UserId, cfg)
if err != nil {
c.Err = err
return
}
ReturnStatusOK(w)
}
func testSiteURL(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT)
return
}
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("testSiteURL", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
}
props := model.MapFromJson(r.Body)
siteURL := props["site_url"]
if siteURL == "" {
c.SetInvalidParam("site_url")
return
}
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT) && siteURL != *c.App.Config().ServiceSettings.SiteURL {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT)
return
}
err := c.App.TestSiteURL(siteURL)
if err != nil {
c.Err = err
return
}
ReturnStatusOK(w)
}
func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec := c.MakeAuditRecord("getAudits", audit.Fail)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_READ_COMPLIANCE) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_COMPLIANCE)
return
}
audits, err := c.App.GetAuditsPage("", c.Params.Page, c.Params.PerPage)
if err != nil {
c.Err = err
return
}
auditRec.Success()
auditRec.AddMeta("page", c.Params.Page)
auditRec.AddMeta("audits_per_page", c.Params.LogsPerPage)
w.Write([]byte(audits.ToJson()))
}
func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT)
return
}
auditRec := c.MakeAuditRecord("databaseRecycle", audit.Fail)
defer c.LogAuditRec(auditRec)
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("databaseRecycle", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
}
c.App.RecycleDatabaseConnection()
auditRec.Success()
ReturnStatusOK(w)
}
func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT)
return
}
auditRec := c.MakeAuditRecord("invalidateCaches", audit.Fail)
defer c.LogAuditRec(auditRec)
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("invalidateCaches", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
}
err := c.App.Srv().InvalidateAllCaches()
if err != nil {
c.Err = err
return
}
auditRec.Success()
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
ReturnStatusOK(w)
}
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec := c.MakeAuditRecord("getLogs", audit.Fail)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_READ_REPORTING) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_REPORTING)
return
}
lines, err := c.App.GetLogs(c.Params.Page, c.Params.LogsPerPage)
if err != nil {
c.Err = err
return
}
auditRec.AddMeta("page", c.Params.Page)
auditRec.AddMeta("logs_per_page", c.Params.LogsPerPage)
w.Write([]byte(model.ArrayToJson(lines)))
}
func postLog(c *Context, w http.ResponseWriter, r *http.Request) {
forceToDebug := false
if !*c.App.Config().ServiceSettings.EnableDeveloper {
if c.App.Session().UserId == "" {
c.Err = model.NewAppError("postLog", "api.context.permissions.app_error", nil, "", http.StatusForbidden)
return
}
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
forceToDebug = true
}
}
m := model.MapFromJson(r.Body)
lvl := m["level"]
msg := m["message"]
if len(msg) > 400 {
msg = msg[0:399]
}
msg = "Client Logs API Endpoint Message: " + msg
fields := []mlog.Field{
mlog.String("type", "client_message"),
mlog.String("user_agent", c.App.UserAgent()),
}
if !forceToDebug && lvl == "ERROR" {
mlog.Error(msg, fields...)
} else {
mlog.Debug(msg, fields...)
}
m["message"] = msg
w.Write([]byte(model.MapToJson(m)))
}
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
teamId := r.URL.Query().Get("team_id")
if name == "" {
name = "standard"
}
permissions := []*model.Permission{
model.PERMISSION_SYSCONSOLE_READ_REPORTING,
model.PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS,
}
if !c.App.SessionHasPermissionToAny(*c.App.Session(), permissions) {
c.SetPermissionError(permissions...)
return
}
rows, err := c.App.GetAnalytics(name, teamId)
if err != nil {
c.Err = err
return
}
if rows == nil {
c.SetInvalidParam("name")
return
}
w.Write([]byte(rows.ToJson()))
}
func getSupportedTimezones(c *Context, w http.ResponseWriter, r *http.Request) {
supportedTimezones := c.App.Timezones().GetSupported()
if supportedTimezones == nil {
supportedTimezones = make([]string, 0)
}
b, err := json.Marshal(supportedTimezones)
if err != nil {
c.Log.Warn("Unable to marshal JSON in timezones.", mlog.Err(err))
w.WriteHeader(http.StatusInternalServerError)
}
w.Write(b)
}
func testS3(c *Context, w http.ResponseWriter, r *http.Request) {
cfg := model.ConfigFromJson(r.Body)
if cfg == nil {
cfg = c.App.Config()
}
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT) {
c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT)
return
}
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("testS3", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
}
err := filesstore.CheckMandatoryS3Fields(&cfg.FileSettings)
if err != nil {
c.Err = err
return
}
if *cfg.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING {
cfg.FileSettings.AmazonS3SecretAccessKey = c.App.Config().FileSettings.AmazonS3SecretAccessKey
}
license := c.App.Srv().License()
backend, appErr := filesstore.NewFileBackend(&cfg.FileSettings, license != nil && *license.Features.Compliance)
if appErr == nil {
appErr = backend.TestConnection()
}
if appErr != nil {
c.Err = appErr
return
}
ReturnStatusOK(w)
}
func getRedirectLocation(c *Context, w http.ResponseWriter, r *http.Request) {
m := make(map[string]string)
m["location"] = ""
if !*c.App.Config().ServiceSettings.EnableLinkPreviews {
w.Write([]byte(model.MapToJson(m)))
return
}
url := r.URL.Query().Get("url")
if len(url) == 0 {
c.SetInvalidParam("url")
return
}
var location string
if err := redirectLocationDataCache.Get(url, &location); err == nil {
m["location"] = location
w.Write([]byte(model.MapToJson(m)))
return
}
client := c.App.HTTPService().MakeClient(false)
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
res, err := client.Head(url)
if err != nil {
// Cache failures to prevent retries.
redirectLocationDataCache.SetWithExpiry(url, "", 1*time.Hour)
// Always return a success status and a JSON string to limit information returned to client.
w.Write([]byte(model.MapToJson(m)))
return
}
defer func() {
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
}()
location = res.Header.Get("Location")
redirectLocationDataCache.SetWithExpiry(url, location, 1*time.Hour)
m["location"] = location
w.Write([]byte(model.MapToJson(m)))
}
func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) {
ack, err := model.PushNotificationAckFromJson(r.Body)
if err != nil {
c.Err = model.NewAppError("pushNotificationAck",
"api.push_notifications_ack.message.parse.app_error",
nil,
err.Error(),
http.StatusBadRequest,
)
return
}
if !*c.App.Config().EmailSettings.SendPushNotifications {
c.Err = model.NewAppError("pushNotificationAck", "api.push_notification.disabled.app_error", nil, "", http.StatusNotImplemented)
return
}
err = c.App.SendAckToPushProxy(ack)
if ack.IsIdLoaded {
if err != nil {
// Log the error only, then continue to fetch notification message
c.App.NotificationsLog().Error("Notification ack not sent to push proxy",
mlog.String("ackId", ack.Id),
mlog.String("type", ack.NotificationType),
mlog.String("postId", ack.PostId),
mlog.String("status", err.Error()),
)
}
notificationInterface := c.App.Notification()
if notificationInterface == nil {
c.Err = model.NewAppError("pushNotificationAck", "api.system.id_loaded.not_available.app_error", nil, "", http.StatusFound)
return
}
msg, appError := notificationInterface.GetNotificationMessage(ack, c.App.Session().UserId)
if appError != nil {
c.Err = model.NewAppError("pushNotificationAck", "api.push_notification.id_loaded.fetch.app_error", nil, appError.Error(), http.StatusInternalServerError)
return
}
w.Write([]byte(msg.ToJson()))
return
} else if err != nil {
c.Err = model.NewAppError("pushNotificationAck", "api.push_notifications_ack.forward.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
ReturnStatusOK(w)
}
func setServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
// number of seconds to keep server marked busy
secs := r.URL.Query().Get("seconds")
if secs == "" {
secs = strconv.FormatInt(DEFAULT_SERVER_BUSY_SECONDS, 10)
}
i, err := strconv.ParseInt(secs, 10, 64)
if err != nil || i <= 0 || i > MAX_SERVER_BUSY_SECONDS {
c.SetInvalidUrlParam(fmt.Sprintf("seconds must be 1 - %d", MAX_SERVER_BUSY_SECONDS))
return
}
auditRec := c.MakeAuditRecord("setServerBusy", audit.Fail)
defer c.LogAuditRec(auditRec)
auditRec.AddMeta("seconds", i)
c.App.Srv().Busy.Set(time.Second * time.Duration(i))
mlog.Warn("server busy state activated - non-critical services disabled", mlog.Int64("seconds", i))
auditRec.Success()
ReturnStatusOK(w)
}
func clearServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
auditRec := c.MakeAuditRecord("clearServerBusy", audit.Fail)
defer c.LogAuditRec(auditRec)
c.App.Srv().Busy.Clear()
mlog.Info("server busy state cleared - non-critical services enabled")
auditRec.Success()
ReturnStatusOK(w)
}
func getServerBusyExpires(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
w.Write([]byte(c.App.Srv().Busy.ToJson()))
}
func upgradeToEnterprise(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec := c.MakeAuditRecord("upgradeToEnterprise", audit.Fail)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
if model.BuildEnterpriseReady == "true" {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.already-enterprise.app_error", nil, "", http.StatusTooManyRequests)
return
}
percentage, _ := c.App.Srv().UpgradeToE0Status()
if percentage > 0 {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.app_error", nil, "", http.StatusTooManyRequests)
return
}
if percentage == 100 {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.already-done.app_error", nil, "", http.StatusTooManyRequests)
return
}
if err := c.App.Srv().CanIUpgradeToE0(); err != nil {
var ipErr *upgrader.InvalidPermissions
var iaErr *upgrader.InvalidArch
switch {
case errors.As(err, &ipErr):
params := map[string]interface{}{
"MattermostUsername": ipErr.MattermostUsername,
"FileUsername": ipErr.FileUsername,
"Path": ipErr.Path,
}
if ipErr.ErrType == "invalid-user-and-permission" {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-user-and-permission.app_error", params, err.Error(), http.StatusForbidden)
} else if ipErr.ErrType == "invalid-user" {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-user.app_error", params, err.Error(), http.StatusForbidden)
} else if ipErr.ErrType == "invalid-permission" {
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-permission.app_error", params, err.Error(), http.StatusForbidden)
}
case errors.As(err, &iaErr):
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.system_not_supported.app_error", nil, err.Error(), http.StatusForbidden)
default:
c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.generic_error.app_error", nil, err.Error(), http.StatusForbidden)
}
return
}
c.App.Srv().Go(func() {
c.App.Srv().UpgradeToE0()
})
auditRec.Success()
w.WriteHeader(http.StatusAccepted)
ReturnStatusOK(w)
}
func upgradeToEnterpriseStatus(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
percentage, err := c.App.Srv().UpgradeToE0Status()
var s map[string]interface{}
if err != nil {
var isErr *upgrader.InvalidSignature
switch {
case errors.As(err, &isErr):
appErr := model.NewAppError("upgradeToEnterpriseStatus", "api.upgrade_to_enterprise_status.app_error", nil, err.Error(), http.StatusBadRequest)
s = map[string]interface{}{"percentage": 0, "error": appErr.Message}
default:
appErr := model.NewAppError("upgradeToEnterpriseStatus", "api.upgrade_to_enterprise_status.signature.app_error", nil, err.Error(), http.StatusBadRequest)
s = map[string]interface{}{"percentage": 0, "error": appErr.Message}
}
} else {
s = map[string]interface{}{"percentage": percentage, "error": nil}
}
w.Write([]byte(model.StringInterfaceToJson(s)))
}
func restart(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec := c.MakeAuditRecord("restartServer", audit.Fail)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
auditRec.Success()
ReturnStatusOK(w)
time.Sleep(1 * time.Second)
go func() {
c.App.Srv().Restart()
}()
}
func getWarnMetricsStatus(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionToAny(*c.App.Session(), model.SysconsoleReadPermissions) {
c.SetPermissionError(model.SysconsoleReadPermissions...)
return
}
license := c.App.Srv().License()
if license != nil {
mlog.Debug("License is present, skip.")
return
}
status, err := c.App.GetWarnMetricsStatus()
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.MapWarnMetricStatusToJson(status)))
}
func sendWarnMetricAckEmail(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec := c.MakeAuditRecord("sendWarnMetricAckEmail", audit.Fail)
defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
license := c.App.Srv().License()
if license != nil {
mlog.Debug("License is present, skip.")
return
}
user, appErr := c.App.GetUser(c.App.Session().UserId)
if appErr != nil {
c.Err = appErr
return
}
ack := model.SendWarnMetricAckFromJson(r.Body)
if ack == nil {
c.SetInvalidParam("ack")
return
}
appErr = c.App.NotifyAndSetWarnMetricAck(c.Params.WarnMetricId, user, ack.ForceAck, false)
if appErr != nil {
c.Err = appErr
}
auditRec.Success()
ReturnStatusOK(w)
}