github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/plugin_requests.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"path"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/gorilla/mux"
    16  
    17  	"github.com/mattermost/mattermost-server/v5/mlog"
    18  	"github.com/mattermost/mattermost-server/v5/model"
    19  	"github.com/mattermost/mattermost-server/v5/plugin"
    20  	"github.com/mattermost/mattermost-server/v5/utils"
    21  )
    22  
    23  func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
    24  	pluginsEnvironment := a.GetPluginsEnvironment()
    25  	if pluginsEnvironment == nil {
    26  		err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented)
    27  		a.Log().Error(err.Error())
    28  		w.WriteHeader(err.StatusCode)
    29  		w.Header().Set("Content-Type", "application/json")
    30  		w.Write([]byte(err.ToJson()))
    31  		return
    32  	}
    33  
    34  	params := mux.Vars(r)
    35  	hooks, err := pluginsEnvironment.HooksForPlugin(params["plugin_id"])
    36  	if err != nil {
    37  		a.Log().Error("Access to route for non-existent plugin",
    38  			mlog.String("missing_plugin_id", params["plugin_id"]),
    39  			mlog.String("url", r.URL.String()),
    40  			mlog.Err(err))
    41  		http.NotFound(w, r)
    42  		return
    43  	}
    44  
    45  	a.servePluginRequest(w, r, hooks.ServeHTTP)
    46  }
    47  
    48  func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) {
    49  	pluginsEnvironment := a.GetPluginsEnvironment()
    50  	if pluginsEnvironment == nil {
    51  		err := model.NewAppError("ServeInterPluginRequest", "app.plugin.disabled.app_error", nil, "Plugin environment not found.", http.StatusNotImplemented)
    52  		a.Log().Error(err.Error())
    53  		w.WriteHeader(err.StatusCode)
    54  		w.Header().Set("Content-Type", "application/json")
    55  		w.Write([]byte(err.ToJson()))
    56  		return
    57  	}
    58  
    59  	hooks, err := pluginsEnvironment.HooksForPlugin(destinationPluginId)
    60  	if err != nil {
    61  		a.Log().Error("Access to route for non-existent plugin in inter plugin request",
    62  			mlog.String("source_plugin_id", sourcePluginId),
    63  			mlog.String("destination_plugin_id", destinationPluginId),
    64  			mlog.String("url", r.URL.String()),
    65  			mlog.Err(err),
    66  		)
    67  		http.NotFound(w, r)
    68  		return
    69  	}
    70  
    71  	context := &plugin.Context{
    72  		RequestId:      model.NewId(),
    73  		UserAgent:      r.UserAgent(),
    74  		SourcePluginId: sourcePluginId,
    75  	}
    76  
    77  	hooks.ServeHTTP(context, w, r)
    78  }
    79  
    80  // ServePluginPublicRequest serves public plugin files
    81  // at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
    82  func (a *App) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
    83  	if strings.HasSuffix(r.URL.Path, "/") {
    84  		http.NotFound(w, r)
    85  		return
    86  	}
    87  
    88  	// Should be in the form of /$PLUGIN_ID/public/{anything} by the time we get here
    89  	vars := mux.Vars(r)
    90  	pluginID := vars["plugin_id"]
    91  
    92  	publicFilesPath, err := a.GetPluginsEnvironment().PublicFilesPath(pluginID)
    93  	if err != nil {
    94  		http.NotFound(w, r)
    95  		return
    96  	}
    97  
    98  	publicFilePath := path.Clean(r.URL.Path)
    99  	prefix := fmt.Sprintf("/plugins/%s/public/", pluginID)
   100  	if !strings.HasPrefix(publicFilePath, prefix) {
   101  		http.NotFound(w, r)
   102  		return
   103  	}
   104  	publicFile := filepath.Join(publicFilesPath, strings.TrimPrefix(publicFilePath, prefix))
   105  	http.ServeFile(w, r, publicFile)
   106  }
   107  
   108  func (a *App) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
   109  	token := ""
   110  	context := &plugin.Context{
   111  		RequestId:      model.NewId(),
   112  		IpAddress:      utils.GetIpAddress(r, a.Config().ServiceSettings.TrustedProxyIPHeader),
   113  		AcceptLanguage: r.Header.Get("Accept-Language"),
   114  		UserAgent:      r.UserAgent(),
   115  	}
   116  	cookieAuth := false
   117  
   118  	authHeader := r.Header.Get(model.HEADER_AUTH)
   119  	if strings.HasPrefix(strings.ToUpper(authHeader), model.HEADER_BEARER+" ") {
   120  		token = authHeader[len(model.HEADER_BEARER)+1:]
   121  	} else if strings.HasPrefix(strings.ToLower(authHeader), model.HEADER_TOKEN+" ") {
   122  		token = authHeader[len(model.HEADER_TOKEN)+1:]
   123  	} else if cookie, _ := r.Cookie(model.SESSION_COOKIE_TOKEN); cookie != nil {
   124  		token = cookie.Value
   125  		cookieAuth = true
   126  	} else {
   127  		token = r.URL.Query().Get("access_token")
   128  	}
   129  
   130  	r.Header.Del("Mattermost-User-Id")
   131  	if token != "" {
   132  		session, err := a.GetSession(token)
   133  		defer ReturnSessionToPool(session)
   134  
   135  		csrfCheckPassed := false
   136  
   137  		if err == nil && cookieAuth && r.Method != "GET" {
   138  			sentToken := ""
   139  
   140  			if r.Header.Get(model.HEADER_CSRF_TOKEN) == "" {
   141  				bodyBytes, _ := ioutil.ReadAll(r.Body)
   142  				r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
   143  				r.ParseForm()
   144  				sentToken = r.FormValue("csrf")
   145  				r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
   146  			} else {
   147  				sentToken = r.Header.Get(model.HEADER_CSRF_TOKEN)
   148  			}
   149  
   150  			expectedToken := session.GetCSRF()
   151  
   152  			if sentToken == expectedToken {
   153  				csrfCheckPassed = true
   154  			}
   155  
   156  			// ToDo(DSchalla) 2019/01/04: Remove after deprecation period and only allow CSRF Header (MM-13657)
   157  			if r.Header.Get(model.HEADER_REQUESTED_WITH) == model.HEADER_REQUESTED_WITH_XML && !csrfCheckPassed {
   158  				csrfErrorMessage := "CSRF Check failed for request - Please migrate your plugin to either send a CSRF Header or Form Field, XMLHttpRequest is deprecated"
   159  				sid := ""
   160  				userID := ""
   161  
   162  				if session != nil {
   163  					sid = session.Id
   164  					userID = session.UserId
   165  				}
   166  
   167  				fields := []mlog.Field{
   168  					mlog.String("path", r.URL.Path),
   169  					mlog.String("ip", r.RemoteAddr),
   170  					mlog.String("session_id", sid),
   171  					mlog.String("user_id", userID),
   172  				}
   173  
   174  				if *a.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement {
   175  					a.Log().Warn(csrfErrorMessage, fields...)
   176  				} else {
   177  					a.Log().Debug(csrfErrorMessage, fields...)
   178  					csrfCheckPassed = true
   179  				}
   180  			}
   181  		} else {
   182  			csrfCheckPassed = true
   183  		}
   184  
   185  		if (session != nil && session.Id != "") && err == nil && csrfCheckPassed {
   186  			r.Header.Set("Mattermost-User-Id", session.UserId)
   187  			context.SessionId = session.Id
   188  		}
   189  	}
   190  
   191  	cookies := r.Cookies()
   192  	r.Header.Del("Cookie")
   193  	for _, c := range cookies {
   194  		if c.Name != model.SESSION_COOKIE_TOKEN {
   195  			r.AddCookie(c)
   196  		}
   197  	}
   198  	r.Header.Del(model.HEADER_AUTH)
   199  	r.Header.Del("Referer")
   200  
   201  	params := mux.Vars(r)
   202  
   203  	subpath, _ := utils.GetSubpathFromConfig(a.Config())
   204  
   205  	newQuery := r.URL.Query()
   206  	newQuery.Del("access_token")
   207  	r.URL.RawQuery = newQuery.Encode()
   208  	r.URL.Path = strings.TrimPrefix(r.URL.Path, path.Join(subpath, "plugins", params["plugin_id"]))
   209  
   210  	handler(context, w, r)
   211  }