2018-06-25 12:33:13 -07:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
2019-04-05 07:35:51 -07:00
"fmt"
2018-06-25 12:33:13 -07:00
"net/http"
2018-07-26 08:33:56 -04:00
"path"
2019-04-05 07:35:51 -07:00
"path/filepath"
2018-06-25 12:33:13 -07:00
"strings"
2018-08-02 00:16:04 +02:00
"bytes"
2018-11-06 00:28:55 -08:00
"io/ioutil"
2018-06-25 12:33:13 -07:00
"github.com/gorilla/mux"
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/plugin"
"github.com/mattermost/mattermost-server/v5/utils"
2018-06-25 12:33:13 -07:00
)
func ( a * App ) ServePluginRequest ( w http . ResponseWriter , r * http . Request ) {
2018-11-20 08:52:51 -05:00
pluginsEnvironment := a . GetPluginsEnvironment ( )
if pluginsEnvironment == nil {
2018-06-25 12:33:13 -07:00
err := model . NewAppError ( "ServePluginRequest" , "app.plugin.disabled.app_error" , nil , "Enable plugins to serve plugin requests" , http . StatusNotImplemented )
2020-02-13 13:26:58 +01:00
a . Log ( ) . Error ( err . Error ( ) )
2018-06-25 12:33:13 -07:00
w . WriteHeader ( err . StatusCode )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Write ( [ ] byte ( err . ToJson ( ) ) )
return
}
params := mux . Vars ( r )
2018-11-20 08:52:51 -05:00
hooks , err := pluginsEnvironment . HooksForPlugin ( params [ "plugin_id" ] )
2018-06-25 12:33:13 -07:00
if err != nil {
2020-02-13 13:26:58 +01:00
a . Log ( ) . Error ( "Access to route for non-existent plugin" , mlog . String ( "missing_plugin_id" , params [ "plugin_id" ] ) , mlog . Err ( err ) )
2018-06-25 12:33:13 -07:00
http . NotFound ( w , r )
return
}
a . servePluginRequest ( w , r , hooks . ServeHTTP )
}
2019-11-04 17:35:58 -08:00
func ( a * App ) ServeInterPluginRequest ( w http . ResponseWriter , r * http . Request , sourcePluginId , destinationPluginId string ) {
pluginsEnvironment := a . GetPluginsEnvironment ( )
if pluginsEnvironment == nil {
2020-03-02 17:10:41 +01:00
err := model . NewAppError ( "ServeInterPluginRequest" , "app.plugin.disabled.app_error" , nil , "Plugin environment not found." , http . StatusNotImplemented )
2020-02-13 13:26:58 +01:00
a . Log ( ) . Error ( err . Error ( ) )
2019-11-04 17:35:58 -08:00
w . WriteHeader ( err . StatusCode )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Write ( [ ] byte ( err . ToJson ( ) ) )
return
}
hooks , err := pluginsEnvironment . HooksForPlugin ( destinationPluginId )
if err != nil {
2020-02-13 13:26:58 +01:00
a . Log ( ) . Error ( "Access to route for non-existent plugin in inter plugin request" ,
2020-01-24 00:06:57 +01:00
mlog . String ( "source_plugin_id" , sourcePluginId ) ,
2019-11-04 17:35:58 -08:00
mlog . String ( "destination_plugin_id" , destinationPluginId ) ,
mlog . Err ( err ) ,
)
http . NotFound ( w , r )
return
}
context := & plugin . Context {
RequestId : model . NewId ( ) ,
UserAgent : r . UserAgent ( ) ,
SourcePluginId : sourcePluginId ,
}
hooks . ServeHTTP ( context , w , r )
}
2019-04-05 07:35:51 -07:00
// ServePluginPublicRequest serves public plugin files
// at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
func ( a * App ) ServePluginPublicRequest ( w http . ResponseWriter , r * http . Request ) {
if strings . HasSuffix ( r . URL . Path , "/" ) {
http . NotFound ( w , r )
return
}
// Should be in the form of /$PLUGIN_ID/public/{anything} by the time we get here
vars := mux . Vars ( r )
pluginID := vars [ "plugin_id" ]
publicFilesPath , err := a . GetPluginsEnvironment ( ) . PublicFilesPath ( pluginID )
if err != nil {
http . NotFound ( w , r )
return
}
publicFilePath := path . Clean ( r . URL . Path )
prefix := fmt . Sprintf ( "/plugins/%s/public/" , pluginID )
if ! strings . HasPrefix ( publicFilePath , prefix ) {
http . NotFound ( w , r )
return
}
publicFile := filepath . Join ( publicFilesPath , strings . TrimPrefix ( publicFilePath , prefix ) )
http . ServeFile ( w , r , publicFile )
}
2018-07-06 06:07:09 -07:00
func ( a * App ) servePluginRequest ( w http . ResponseWriter , r * http . Request , handler func ( * plugin . Context , http . ResponseWriter , * http . Request ) ) {
2018-06-25 12:33:13 -07:00
token := ""
2018-12-05 10:46:08 -08:00
context := & plugin . Context {
RequestId : model . NewId ( ) ,
2019-05-24 20:22:13 +02:00
IpAddress : utils . GetIpAddress ( r , a . Config ( ) . ServiceSettings . TrustedProxyIPHeader ) ,
2018-12-05 10:46:08 -08:00
AcceptLanguage : r . Header . Get ( "Accept-Language" ) ,
UserAgent : r . UserAgent ( ) ,
}
2018-08-02 00:16:04 +02:00
cookieAuth := false
2018-06-25 12:33:13 -07:00
authHeader := r . Header . Get ( model . HEADER_AUTH )
if strings . HasPrefix ( strings . ToUpper ( authHeader ) , model . HEADER_BEARER + " " ) {
token = authHeader [ len ( model . HEADER_BEARER ) + 1 : ]
} else if strings . HasPrefix ( strings . ToLower ( authHeader ) , model . HEADER_TOKEN + " " ) {
token = authHeader [ len ( model . HEADER_TOKEN ) + 1 : ]
2018-08-02 00:16:04 +02:00
} else if cookie , _ := r . Cookie ( model . SESSION_COOKIE_TOKEN ) ; cookie != nil {
2018-06-25 12:33:13 -07:00
token = cookie . Value
2018-08-02 00:16:04 +02:00
cookieAuth = true
2018-06-25 12:33:13 -07:00
} else {
token = r . URL . Query ( ) . Get ( "access_token" )
}
r . Header . Del ( "Mattermost-User-Id" )
if token != "" {
2018-08-02 00:16:04 +02:00
session , err := a . GetSession ( token )
2019-01-31 20:39:02 +01:00
csrfCheckPassed := false
if err == nil && cookieAuth && r . Method != "GET" {
sentToken := ""
if r . Header . Get ( model . HEADER_CSRF_TOKEN ) == "" {
bodyBytes , _ := ioutil . ReadAll ( r . Body )
r . Body = ioutil . NopCloser ( bytes . NewBuffer ( bodyBytes ) )
r . ParseForm ( )
sentToken = r . FormValue ( "csrf" )
r . Body = ioutil . NopCloser ( bytes . NewBuffer ( bodyBytes ) )
} else {
sentToken = r . Header . Get ( model . HEADER_CSRF_TOKEN )
}
2018-08-02 00:16:04 +02:00
expectedToken := session . GetCSRF ( )
2019-01-31 20:39:02 +01:00
if sentToken == expectedToken {
csrfCheckPassed = true
2018-08-02 00:16:04 +02:00
}
2019-01-31 20:39:02 +01:00
// ToDo(DSchalla) 2019/01/04: Remove after deprecation period and only allow CSRF Header (MM-13657)
2019-02-20 15:21:19 +01:00
if r . Header . Get ( model . HEADER_REQUESTED_WITH ) == model . HEADER_REQUESTED_WITH_XML && ! csrfCheckPassed {
csrfErrorMessage := "CSRF Check failed for request - Please migrate your plugin to either send a CSRF Header or Form Field, XMLHttpRequest is deprecated"
2019-07-17 15:08:58 +02:00
sid := ""
userId := ""
if session != nil {
sid = session . Id
userId = session . UserId
}
fields := [ ] mlog . Field {
mlog . String ( "path" , r . URL . Path ) ,
mlog . String ( "ip" , r . RemoteAddr ) ,
mlog . String ( "session_id" , sid ) ,
mlog . String ( "user_id" , userId ) ,
}
2019-02-20 15:21:19 +01:00
if * a . Config ( ) . ServiceSettings . ExperimentalStrictCSRFEnforcement {
2020-02-13 13:26:58 +01:00
a . Log ( ) . Warn ( csrfErrorMessage , fields ... )
2019-02-20 15:21:19 +01:00
} else {
2020-02-13 13:26:58 +01:00
a . Log ( ) . Debug ( csrfErrorMessage , fields ... )
2019-02-20 15:21:19 +01:00
csrfCheckPassed = true
}
2019-01-31 20:39:02 +01:00
}
} else {
csrfCheckPassed = true
2018-08-02 00:16:04 +02:00
}
if session != nil && err == nil && csrfCheckPassed {
2018-06-25 12:33:13 -07:00
r . Header . Set ( "Mattermost-User-Id" , session . UserId )
2018-08-02 00:16:04 +02:00
context . SessionId = session . Id
2018-06-25 12:33:13 -07:00
}
}
cookies := r . Cookies ( )
r . Header . Del ( "Cookie" )
for _ , c := range cookies {
if c . Name != model . SESSION_COOKIE_TOKEN {
r . AddCookie ( c )
}
}
r . Header . Del ( model . HEADER_AUTH )
r . Header . Del ( "Referer" )
params := mux . Vars ( r )
2018-07-26 08:33:56 -04:00
subpath , _ := utils . GetSubpathFromConfig ( a . Config ( ) )
2018-06-25 12:33:13 -07:00
newQuery := r . URL . Query ( )
newQuery . Del ( "access_token" )
r . URL . RawQuery = newQuery . Encode ( )
2018-07-26 08:33:56 -04:00
r . URL . Path = strings . TrimPrefix ( r . URL . Path , path . Join ( subpath , "plugins" , params [ "plugin_id" ] ) )
2018-06-25 12:33:13 -07:00
2018-08-02 00:16:04 +02:00
handler ( context , w , r )
2018-06-25 12:33:13 -07:00
}