2019-11-29 12:59:40 +01:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2017-02-17 11:57:19 +01:00
package api4
import (
2018-12-17 08:51:46 -08:00
"encoding/json"
2018-04-27 12:49:45 -07:00
"fmt"
2020-05-20 08:41:37 +05:30
"io"
"io/ioutil"
2017-02-17 11:57:19 +01:00
"net/http"
2017-05-31 06:47:27 +02:00
"runtime"
2019-11-27 20:41:09 -05:00
"strconv"
2019-08-23 00:25:50 +02:00
"time"
2017-02-17 11:57:19 +01:00
2020-08-21 20:23:04 +02:00
"github.com/pkg/errors"
2020-03-12 15:50:21 -04:00
"github.com/mattermost/mattermost-server/v5/audit"
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
2020-07-18 01:01:06 -07:00
"github.com/mattermost/mattermost-server/v5/services/cache"
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/services/filesstore"
2020-08-21 20:23:04 +02:00
"github.com/mattermost/mattermost-server/v5/services/upgrader"
2017-02-17 11:57:19 +01:00
)
2019-11-27 20:41:09 -05:00
const (
REDIRECT_LOCATION_CACHE_SIZE = 10000
DEFAULT_SERVER_BUSY_SECONDS = 3600
MAX_SERVER_BUSY_SECONDS = 86400
)
2019-01-27 18:24:46 -08:00
2020-07-18 01:01:06 -07:00
var redirectLocationDataCache = cache . NewLRU ( & cache . LRUOptions {
2020-06-02 09:01:30 -04:00
Size : REDIRECT_LOCATION_CACHE_SIZE ,
} )
2019-01-27 18:24:46 -08:00
2017-09-22 12:54:27 -05:00
func ( api * API ) InitSystem ( ) {
api . BaseRoutes . System . Handle ( "/ping" , api . ApiHandler ( getSystemPing ) ) . Methods ( "GET" )
2017-05-31 06:47:27 +02:00
2018-03-22 06:53:43 -07:00
api . BaseRoutes . System . Handle ( "/timezones" , api . ApiSessionRequired ( getSupportedTimezones ) ) . Methods ( "GET" )
2017-09-22 12:54:27 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/audits" , api . ApiSessionRequired ( getAudits ) ) . Methods ( "GET" )
api . BaseRoutes . ApiRoot . Handle ( "/email/test" , api . ApiSessionRequired ( testEmail ) ) . Methods ( "POST" )
2019-09-06 12:52:14 +02:00
api . BaseRoutes . ApiRoot . Handle ( "/site_url/test" , api . ApiSessionRequired ( testSiteURL ) ) . Methods ( "POST" )
2018-03-01 00:12:11 +01:00
api . BaseRoutes . ApiRoot . Handle ( "/file/s3_test" , api . ApiSessionRequired ( testS3 ) ) . Methods ( "POST" )
2017-09-22 12:54:27 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/database/recycle" , api . ApiSessionRequired ( databaseRecycle ) ) . Methods ( "POST" )
api . BaseRoutes . ApiRoot . Handle ( "/caches/invalidate" , api . ApiSessionRequired ( invalidateCaches ) ) . Methods ( "POST" )
2017-03-16 14:59:44 -04:00
2017-09-22 12:54:27 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/logs" , api . ApiSessionRequired ( getLogs ) ) . Methods ( "GET" )
2017-10-20 20:26:12 -04:00
api . BaseRoutes . ApiRoot . Handle ( "/logs" , api . ApiHandler ( postLog ) ) . Methods ( "POST" )
2017-06-19 16:35:53 -04:00
2017-09-22 12:54:27 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/analytics/old" , api . ApiSessionRequired ( getAnalytics ) ) . Methods ( "GET" )
2018-08-24 08:49:31 -04:00
api . BaseRoutes . ApiRoot . Handle ( "/redirect_location" , api . ApiSessionRequiredTrustRequester ( getRedirectLocation ) ) . Methods ( "GET" )
2019-04-30 18:15:29 -04:00
api . BaseRoutes . ApiRoot . Handle ( "/notifications/ack" , api . ApiSessionRequired ( pushNotificationAck ) ) . Methods ( "POST" )
2019-11-27 20:41:09 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/server_busy" , api . ApiSessionRequired ( setServerBusy ) ) . Methods ( "POST" )
api . BaseRoutes . ApiRoot . Handle ( "/server_busy" , api . ApiSessionRequired ( getServerBusyExpires ) ) . Methods ( "GET" )
2020-01-21 10:48:50 -05:00
api . BaseRoutes . ApiRoot . Handle ( "/server_busy" , api . ApiSessionRequired ( clearServerBusy ) ) . Methods ( "DELETE" )
2020-08-21 20:23:04 +02:00
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" )
2020-07-22 20:32:21 -07:00
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" )
2017-02-17 11:57:19 +01:00
}
func getSystemPing ( c * Context , w http . ResponseWriter , r * http . Request ) {
2019-06-20 16:06:04 -04:00
reqs := c . App . Config ( ) . ClientRequirements
2017-05-31 06:47:27 +02:00
2019-06-20 16:06:04 -04:00
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
2017-08-28 09:22:54 -07:00
2019-06-20 16:06:04 -04:00
actualGoroutines := runtime . NumGoroutine ( )
if * c . App . Config ( ) . ServiceSettings . GoroutineHealthThreshold > 0 && actualGoroutines >= * c . App . Config ( ) . ServiceSettings . GoroutineHealthThreshold {
2019-09-11 13:06:52 +02:00
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 ) )
2019-06-20 16:06:04 -04:00
s [ model . STATUS ] = model . STATUS_UNHEALTHY
}
2017-05-31 06:47:27 +02:00
2019-06-20 16:06:04 -04:00
// 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
2019-08-23 00:25:50 +02:00
2020-05-21 16:29:21 -04:00
// Database Write Check
2019-08-23 00:25:50 +02:00
currentTime := fmt . Sprintf ( "%d" , time . Now ( ) . Unix ( ) )
2020-05-05 10:44:18 -04:00
healthCheckKey := fmt . Sprintf ( "health_check_%s" , c . App . GetClusterId ( ) )
2019-08-23 00:25:50 +02:00
2020-02-13 13:26:58 +01:00
writeErr := c . App . Srv ( ) . Store . System ( ) . SaveOrUpdate ( & model . System {
2019-08-23 00:25:50 +02:00
Name : healthCheckKey ,
Value : currentTime ,
} )
if writeErr != nil {
2020-05-15 14:19:46 +05:30
mlog . Warn ( "Unable to write to database." , mlog . Err ( writeErr ) )
2019-06-20 16:06:04 -04:00
s [ dbStatusKey ] = model . STATUS_UNHEALTHY
s [ model . STATUS ] = model . STATUS_UNHEALTHY
2020-05-21 16:29:21 -04:00
}
2020-05-05 10:44:18 -04:00
2020-05-21 16:29:21 -04:00
_ , 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." )
2019-06-20 16:06:04 -04:00
}
filestoreStatusKey := "filestore_status"
s [ filestoreStatusKey ] = model . STATUS_OK
2020-06-12 13:43:50 +02:00
license := c . App . Srv ( ) . License ( )
2019-06-20 16:06:04 -04:00
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 {
2019-09-11 13:06:52 +02:00
mlog . Debug ( "Unable to get filestore for ping status." , mlog . Err ( appErr ) )
2019-06-20 16:06:04 -04:00
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 ] )
}
2017-05-31 06:47:27 +02:00
2019-06-20 16:06:04 -04:00
if s [ model . STATUS ] != model . STATUS_OK {
2017-05-31 06:47:27 +02:00
w . WriteHeader ( http . StatusInternalServerError )
}
2019-06-20 16:06:04 -04:00
w . Write ( [ ] byte ( model . MapToJson ( s ) ) )
2017-02-17 11:57:19 +01:00
}
2017-03-13 13:27:27 +01:00
2017-03-13 16:09:00 +01:00
func testEmail ( c * Context , w http . ResponseWriter , r * http . Request ) {
2017-06-14 08:56:56 -04:00
cfg := model . ConfigFromJson ( r . Body )
if cfg == nil {
2017-10-18 15:36:43 -07:00
cfg = c . App . Config ( )
2017-06-14 08:56:56 -04:00
}
2017-03-13 16:09:00 +01:00
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT )
2017-03-13 16:09:00 +01:00
return
}
2019-03-08 13:15:28 -05:00
if * c . App . Config ( ) . ExperimentalSettings . RestrictSystemAdmin {
c . Err = model . NewAppError ( "testEmail" , "api.restricted_system_admin" , nil , "" , http . StatusForbidden )
2017-03-20 13:37:34 +01:00
return
}
2020-02-13 13:26:58 +01:00
err := c . App . TestEmail ( c . App . Session ( ) . UserId , cfg )
2017-03-20 13:37:34 +01:00
if err != nil {
c . Err = err
return
}
2019-03-08 13:15:28 -05:00
ReturnStatusOK ( w )
2017-03-20 13:37:34 +01:00
}
2019-09-06 12:52:14 +02:00
func testSiteURL ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT )
2019-09-06 12:52:14 +02:00
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
}
2020-08-21 16:49:31 -04:00
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
}
2019-09-06 12:52:14 +02:00
err := c . App . TestSiteURL ( siteURL )
if err != nil {
c . Err = err
return
}
ReturnStatusOK ( w )
}
2017-03-21 09:06:08 -04:00
func getAudits ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-04-08 00:52:30 -04:00
auditRec := c . MakeAuditRecord ( "getAudits" , audit . Fail )
defer c . LogAuditRec ( auditRec )
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_READ_COMPLIANCE ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_READ_COMPLIANCE )
2017-03-21 09:06:08 -04:00
return
}
2017-09-06 17:12:54 -05:00
audits , err := c . App . GetAuditsPage ( "" , c . Params . Page , c . Params . PerPage )
2017-03-21 09:06:08 -04:00
if err != nil {
c . Err = err
return
}
2020-04-08 00:52:30 -04:00
auditRec . Success ( )
auditRec . AddMeta ( "page" , c . Params . Page )
auditRec . AddMeta ( "audits_per_page" , c . Params . LogsPerPage )
2017-03-21 09:06:08 -04:00
w . Write ( [ ] byte ( audits . ToJson ( ) ) )
}
2017-03-13 16:47:33 +01:00
func databaseRecycle ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT )
2017-03-13 16:47:33 +01:00
return
}
2020-03-12 15:50:21 -04:00
auditRec := c . MakeAuditRecord ( "databaseRecycle" , audit . Fail )
defer c . LogAuditRec ( auditRec )
2019-03-08 13:15:28 -05:00
if * c . App . Config ( ) . ExperimentalSettings . RestrictSystemAdmin {
c . Err = model . NewAppError ( "databaseRecycle" , "api.restricted_system_admin" , nil , "" , http . StatusForbidden )
return
}
2017-09-06 17:12:54 -05:00
c . App . RecycleDatabaseConnection ( )
2017-03-13 16:47:33 +01:00
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2017-03-13 16:47:33 +01:00
ReturnStatusOK ( w )
}
2017-03-14 17:06:07 +01:00
func invalidateCaches ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT )
2017-03-14 17:06:07 +01:00
return
}
2020-03-12 15:50:21 -04:00
auditRec := c . MakeAuditRecord ( "invalidateCaches" , audit . Fail )
defer c . LogAuditRec ( auditRec )
2019-03-08 13:15:28 -05:00
if * c . App . Config ( ) . ExperimentalSettings . RestrictSystemAdmin {
c . Err = model . NewAppError ( "invalidateCaches" , "api.restricted_system_admin" , nil , "" , http . StatusForbidden )
return
}
2020-06-12 13:43:50 +02:00
err := c . App . Srv ( ) . InvalidateAllCaches ( )
2017-03-14 17:06:07 +01:00
if err != nil {
c . Err = err
return
}
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2017-03-14 17:06:07 +01:00
w . Header ( ) . Set ( "Cache-Control" , "no-cache, no-store, must-revalidate" )
ReturnStatusOK ( w )
}
2017-03-16 14:59:44 -04:00
func getLogs ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-04-08 00:52:30 -04:00
auditRec := c . MakeAuditRecord ( "getLogs" , audit . Fail )
defer c . LogAuditRec ( auditRec )
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_READ_REPORTING ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_READ_REPORTING )
2017-03-16 14:59:44 -04:00
return
}
2017-12-14 04:04:55 +09:00
lines , err := c . App . GetLogs ( c . Params . Page , c . Params . LogsPerPage )
2017-03-16 14:59:44 -04:00
if err != nil {
c . Err = err
return
}
2020-04-08 00:52:30 -04:00
auditRec . AddMeta ( "page" , c . Params . Page )
auditRec . AddMeta ( "logs_per_page" , c . Params . LogsPerPage )
2017-03-16 14:59:44 -04:00
w . Write ( [ ] byte ( model . ArrayToJson ( lines ) ) )
}
2017-03-27 09:19:53 -04:00
2017-04-21 11:16:35 +02:00
func postLog ( c * Context , w http . ResponseWriter , r * http . Request ) {
2017-10-20 20:26:12 -04:00
forceToDebug := false
if ! * c . App . Config ( ) . ServiceSettings . EnableDeveloper {
2020-02-13 13:26:58 +01:00
if c . App . Session ( ) . UserId == "" {
2017-10-20 20:26:12 -04:00
c . Err = model . NewAppError ( "postLog" , "api.context.permissions.app_error" , nil , "" , http . StatusForbidden )
return
}
2020-02-13 13:26:58 +01:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_MANAGE_SYSTEM ) {
2017-10-20 20:26:12 -04:00
forceToDebug = true
}
2017-04-21 11:16:35 +02:00
}
m := model . MapFromJson ( r . Body )
lvl := m [ "level" ]
msg := m [ "message" ]
if len ( msg ) > 400 {
msg = msg [ 0 : 399 ]
}
2019-11-04 19:03:59 +01:00
msg = "Client Logs API Endpoint Message: " + msg
fields := [ ] mlog . Field {
mlog . String ( "type" , "client_message" ) ,
2020-02-13 13:26:58 +01:00
mlog . String ( "user_agent" , c . App . UserAgent ( ) ) ,
2019-11-04 19:03:59 +01:00
}
2017-10-20 20:26:12 -04:00
if ! forceToDebug && lvl == "ERROR" {
2019-11-04 19:03:59 +01:00
mlog . Error ( msg , fields ... )
2017-04-21 11:16:35 +02:00
} else {
2019-11-04 19:03:59 +01:00
mlog . Debug ( msg , fields ... )
2017-04-21 11:16:35 +02:00
}
m [ "message" ] = msg
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
2017-06-19 16:35:53 -04:00
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"
}
2020-08-21 16:49:31 -04:00
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 ... )
2017-06-19 16:35:53 -04:00
return
}
2017-09-06 17:12:54 -05:00
rows , err := c . App . GetAnalytics ( name , teamId )
2017-06-19 16:35:53 -04:00
if err != nil {
c . Err = err
return
}
if rows == nil {
c . SetInvalidParam ( "name" )
return
}
w . Write ( [ ] byte ( rows . ToJson ( ) ) )
}
2018-03-01 00:12:11 +01:00
2018-03-22 06:53:43 -07:00
func getSupportedTimezones ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-02-13 13:26:58 +01:00
supportedTimezones := c . App . Timezones ( ) . GetSupported ( )
2018-12-17 08:51:46 -08:00
if supportedTimezones == nil {
supportedTimezones = make ( [ ] string , 0 )
}
2018-03-22 06:53:43 -07:00
2018-12-17 08:51:46 -08:00
b , err := json . Marshal ( supportedTimezones )
if err != nil {
c . Log . Warn ( "Unable to marshal JSON in timezones." , mlog . Err ( err ) )
w . WriteHeader ( http . StatusInternalServerError )
2018-03-22 06:53:43 -07:00
}
2018-12-17 08:51:46 -08:00
w . Write ( b )
2018-03-22 06:53:43 -07:00
}
2018-03-01 00:12:11 +01:00
func testS3 ( c * Context , w http . ResponseWriter , r * http . Request ) {
cfg := model . ConfigFromJson ( r . Body )
if cfg == nil {
cfg = c . App . Config ( )
}
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT ) {
c . SetPermissionError ( model . PERMISSION_SYSCONSOLE_READ_ENVIRONMENT )
2018-03-01 00:12:11 +01:00
return
}
2019-03-08 13:15:28 -05:00
if * c . App . Config ( ) . ExperimentalSettings . RestrictSystemAdmin {
c . Err = model . NewAppError ( "testS3" , "api.restricted_system_admin" , nil , "" , http . StatusForbidden )
return
}
2018-09-20 19:07:03 +02:00
err := filesstore . CheckMandatoryS3Fields ( & cfg . FileSettings )
2018-03-01 00:12:11 +01:00
if err != nil {
c . Err = err
return
}
2019-01-31 08:12:01 -05:00
if * cfg . FileSettings . AmazonS3SecretAccessKey == model . FAKE_SETTING {
2018-03-13 17:26:56 +01:00
cfg . FileSettings . AmazonS3SecretAccessKey = c . App . Config ( ) . FileSettings . AmazonS3SecretAccessKey
}
2020-06-12 13:43:50 +02:00
license := c . App . Srv ( ) . License ( )
2018-09-20 19:07:03 +02:00
backend , appErr := filesstore . NewFileBackend ( & cfg . FileSettings , license != nil && * license . Features . Compliance )
2018-03-01 00:12:11 +01:00
if appErr == nil {
appErr = backend . TestConnection ( )
}
if appErr != nil {
c . Err = appErr
return
}
ReturnStatusOK ( w )
}
2018-08-24 08:49:31 -04:00
func getRedirectLocation ( c * Context , w http . ResponseWriter , r * http . Request ) {
2018-10-02 09:00:50 +03:00
m := make ( map [ string ] string )
m [ "location" ] = ""
2019-01-27 18:24:46 -08:00
2019-02-12 08:37:54 -05:00
if ! * c . App . Config ( ) . ServiceSettings . EnableLinkPreviews {
2018-10-02 09:00:50 +03:00
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
return
}
2019-01-27 18:24:46 -08:00
2018-08-24 08:49:31 -04:00
url := r . URL . Query ( ) . Get ( "url" )
if len ( url ) == 0 {
c . SetInvalidParam ( "url" )
return
}
2020-06-02 09:01:30 -04:00
var location string
if err := redirectLocationDataCache . Get ( url , & location ) ; err == nil {
m [ "location" ] = location
2019-01-27 18:24:46 -08:00
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
return
}
2020-02-13 13:26:58 +01:00
client := c . App . HTTPService ( ) . MakeClient ( false )
2019-01-27 18:24:46 -08:00
client . CheckRedirect = func ( req * http . Request , via [ ] * http . Request ) error {
return http . ErrUseLastResponse
2018-08-24 08:49:31 -04:00
}
res , err := client . Head ( url )
if err != nil {
2019-01-27 18:24:46 -08:00
// Cache failures to prevent retries.
2020-06-02 09:01:30 -04:00
redirectLocationDataCache . SetWithExpiry ( url , "" , 1 * time . Hour )
2019-01-27 18:24:46 -08:00
// Always return a success status and a JSON string to limit information returned to client.
2018-08-24 08:49:31 -04:00
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
return
}
2020-05-20 08:41:37 +05:30
defer func ( ) {
io . Copy ( ioutil . Discard , res . Body )
res . Body . Close ( )
} ( )
2018-08-24 08:49:31 -04:00
2020-06-02 09:01:30 -04:00
location = res . Header . Get ( "Location" )
redirectLocationDataCache . SetWithExpiry ( url , location , 1 * time . Hour )
2019-01-27 18:24:46 -08:00
m [ "location" ] = location
2018-08-24 08:49:31 -04:00
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
2019-04-30 18:15:29 -04:00
func pushNotificationAck ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-01-29 10:08:27 +01:00
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
}
2019-04-30 18:15:29 -04:00
if ! * c . App . Config ( ) . EmailSettings . SendPushNotifications {
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notification.disabled.app_error" , nil , "" , http . StatusNotImplemented )
return
}
2020-01-29 10:08:27 +01:00
err = c . App . SendAckToPushProxy ( ack )
2019-11-21 19:35:19 -03:00
if ack . IsIdLoaded {
2019-11-18 15:42:51 -08:00
if err != nil {
// Log the error only, then continue to fetch notification message
2020-02-13 13:26:58 +01:00
c . App . NotificationsLog ( ) . Error ( "Notification ack not sent to push proxy" ,
2019-11-18 15:42:51 -08:00
mlog . String ( "ackId" , ack . Id ) ,
mlog . String ( "type" , ack . NotificationType ) ,
mlog . String ( "postId" , ack . PostId ) ,
mlog . String ( "status" , err . Error ( ) ) ,
)
}
2020-02-13 13:26:58 +01:00
notificationInterface := c . App . Notification ( )
2019-11-21 19:35:19 -03:00
if notificationInterface == nil {
c . Err = model . NewAppError ( "pushNotificationAck" , "api.system.id_loaded.not_available.app_error" , nil , "" , http . StatusFound )
return
}
2020-02-13 13:26:58 +01:00
msg , appError := notificationInterface . GetNotificationMessage ( ack , c . App . Session ( ) . UserId )
2019-11-21 19:35:19 -03:00
if appError != nil {
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notification.id_loaded.fetch.app_error" , nil , appError . Error ( ) , http . StatusInternalServerError )
2019-11-18 15:42:51 -08:00
return
}
w . Write ( [ ] byte ( msg . ToJson ( ) ) )
return
} else if err != nil {
2019-04-30 18:15:29 -04:00
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notifications_ack.forward.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
ReturnStatusOK ( w )
}
2019-11-27 20:41:09 -05:00
func setServerBusy ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-02-13 13:26:58 +01:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_MANAGE_SYSTEM ) {
2019-11-27 20:41:09 -05:00
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
}
2020-03-12 15:50:21 -04:00
auditRec := c . MakeAuditRecord ( "setServerBusy" , audit . Fail )
defer c . LogAuditRec ( auditRec )
auditRec . AddMeta ( "seconds" , i )
2020-02-13 13:26:58 +01:00
c . App . Srv ( ) . Busy . Set ( time . Second * time . Duration ( i ) )
2019-11-27 20:41:09 -05:00
mlog . Warn ( "server busy state activated - non-critical services disabled" , mlog . Int64 ( "seconds" , i ) )
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2019-11-27 20:41:09 -05:00
ReturnStatusOK ( w )
}
func clearServerBusy ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-02-13 13:26:58 +01:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_MANAGE_SYSTEM ) {
2019-11-27 20:41:09 -05:00
c . SetPermissionError ( model . PERMISSION_MANAGE_SYSTEM )
return
}
2020-03-12 15:50:21 -04:00
auditRec := c . MakeAuditRecord ( "clearServerBusy" , audit . Fail )
defer c . LogAuditRec ( auditRec )
2020-02-13 13:26:58 +01:00
c . App . Srv ( ) . Busy . Clear ( )
2019-11-27 20:41:09 -05:00
mlog . Info ( "server busy state cleared - non-critical services enabled" )
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2019-11-27 20:41:09 -05:00
ReturnStatusOK ( w )
}
func getServerBusyExpires ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-02-13 13:26:58 +01:00
if ! c . App . SessionHasPermissionTo ( * c . App . Session ( ) , model . PERMISSION_MANAGE_SYSTEM ) {
2019-11-27 20:41:09 -05:00
c . SetPermissionError ( model . PERMISSION_MANAGE_SYSTEM )
return
}
2020-02-13 13:26:58 +01:00
w . Write ( [ ] byte ( c . App . Srv ( ) . Busy . ToJson ( ) ) )
2019-11-27 20:41:09 -05:00
}
2020-07-22 20:32:21 -07:00
2020-08-21 20:23:04 +02:00
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 ( )
} ( )
}
2020-07-22 20:32:21 -07:00
func getWarnMetricsStatus ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-08-21 16:49:31 -04:00
if ! c . App . SessionHasPermissionToAny ( * c . App . Session ( ) , model . SysconsoleReadPermissions ) {
c . SetPermissionError ( model . SysconsoleReadPermissions ... )
2020-07-22 20:32:21 -07:00
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 )
}