github.com/jfrerich/mattermost-server@v5.8.0-rc2+incompatible/web/handlers.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package web
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"time"
    10  
    11  	"github.com/mattermost/mattermost-server/app"
    12  	"github.com/mattermost/mattermost-server/mlog"
    13  	"github.com/mattermost/mattermost-server/model"
    14  	"github.com/mattermost/mattermost-server/utils"
    15  )
    16  
    17  func (w *Web) NewHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
    18  	return &Handler{
    19  		GetGlobalAppOptions: w.GetGlobalAppOptions,
    20  		HandleFunc:          h,
    21  		RequireSession:      false,
    22  		TrustRequester:      false,
    23  		RequireMfa:          false,
    24  		IsStatic:            false,
    25  	}
    26  }
    27  
    28  func (w *Web) NewStaticHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
    29  	return &Handler{
    30  		GetGlobalAppOptions: w.GetGlobalAppOptions,
    31  		HandleFunc:          h,
    32  		RequireSession:      false,
    33  		TrustRequester:      false,
    34  		RequireMfa:          false,
    35  		IsStatic:            true,
    36  	}
    37  }
    38  
    39  type Handler struct {
    40  	GetGlobalAppOptions app.AppOptionCreator
    41  	HandleFunc          func(*Context, http.ResponseWriter, *http.Request)
    42  	RequireSession      bool
    43  	TrustRequester      bool
    44  	RequireMfa          bool
    45  	IsStatic            bool
    46  }
    47  
    48  func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    49  	now := time.Now()
    50  	mlog.Debug(fmt.Sprintf("%v - %v", r.Method, r.URL.Path))
    51  
    52  	c := &Context{}
    53  	c.App = app.New(
    54  		h.GetGlobalAppOptions()...,
    55  	)
    56  	c.App.T, _ = utils.GetTranslationsAndLocale(w, r)
    57  	c.App.RequestId = model.NewId()
    58  	c.App.IpAddress = utils.GetIpAddress(r)
    59  	c.App.UserAgent = r.UserAgent()
    60  	c.App.AcceptLanguage = r.Header.Get("Accept-Language")
    61  	c.Params = ParamsFromRequest(r)
    62  	c.App.Path = r.URL.Path
    63  	c.Log = c.App.Log
    64  
    65  	token, tokenLocation := app.ParseAuthTokenFromRequest(r)
    66  
    67  	// CSRF Check
    68  	if tokenLocation == app.TokenLocationCookie && h.RequireSession && !h.TrustRequester {
    69  		if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML {
    70  			c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized)
    71  			token = ""
    72  		}
    73  	}
    74  
    75  	subpath, _ := utils.GetSubpathFromConfig(c.App.Config())
    76  	siteURLHeader := app.GetProtocol(r) + "://" + r.Host + subpath
    77  	c.SetSiteURLHeader(siteURLHeader)
    78  
    79  	w.Header().Set(model.HEADER_REQUEST_ID, c.App.RequestId)
    80  	w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.License() != nil))
    81  
    82  	if *c.App.Config().ServiceSettings.TLSStrictTransport {
    83  		w.Header().Set("Strict-Transport-Security", fmt.Sprintf("max-age=%d", *c.App.Config().ServiceSettings.TLSStrictTransportMaxAge))
    84  	}
    85  
    86  	if h.IsStatic {
    87  		// Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking
    88  		w.Header().Set("X-Frame-Options", "SAMEORIGIN")
    89  		// Set content security policy. This is also specified in the root.html of the webapp in a meta tag.
    90  		w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'; script-src 'self' cdn.segment.com/analytics.js/")
    91  	} else {
    92  		// All api response bodies will be JSON formatted by default
    93  		w.Header().Set("Content-Type", "application/json")
    94  
    95  		if r.Method == "GET" {
    96  			w.Header().Set("Expires", "0")
    97  		}
    98  	}
    99  
   100  	if len(token) != 0 {
   101  		session, err := c.App.GetSession(token)
   102  
   103  		if err != nil {
   104  			c.Log.Info("Invalid session", mlog.Err(err))
   105  			if err.StatusCode == http.StatusInternalServerError {
   106  				c.Err = err
   107  			} else if h.RequireSession {
   108  				c.RemoveSessionCookie(w, r)
   109  				c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized)
   110  			}
   111  		} else if !session.IsOAuth && tokenLocation == app.TokenLocationQueryString {
   112  			c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized)
   113  		} else {
   114  			c.App.Session = *session
   115  		}
   116  
   117  		// Rate limit by UserID
   118  		if c.App.Srv.RateLimiter != nil && c.App.Srv.RateLimiter.UserIdRateLimit(c.App.Session.UserId, w) {
   119  			return
   120  		}
   121  	}
   122  
   123  	c.Log = c.App.Log.With(
   124  		mlog.String("path", c.App.Path),
   125  		mlog.String("request_id", c.App.RequestId),
   126  		mlog.String("ip_addr", c.App.IpAddress),
   127  		mlog.String("user_id", c.App.Session.UserId),
   128  		mlog.String("method", r.Method),
   129  	)
   130  
   131  	if c.Err == nil && h.RequireSession {
   132  		c.SessionRequired()
   133  	}
   134  
   135  	if c.Err == nil && h.RequireMfa {
   136  		c.MfaRequired()
   137  	}
   138  
   139  	if c.Err == nil {
   140  		h.HandleFunc(c, w, r)
   141  	}
   142  
   143  	// Handle errors that have occurred
   144  	if c.Err != nil {
   145  		c.Err.Translate(c.App.T)
   146  		c.Err.RequestId = c.App.RequestId
   147  
   148  		if c.Err.Id == "api.context.session_expired.app_error" {
   149  			c.LogInfo(c.Err)
   150  		} else {
   151  			c.LogError(c.Err)
   152  		}
   153  
   154  		c.Err.Where = r.URL.Path
   155  
   156  		// Block out detailed error when not in developer mode
   157  		if !*c.App.Config().ServiceSettings.EnableDeveloper {
   158  			c.Err.DetailedError = ""
   159  		}
   160  
   161  		// Sanitize all 5xx error messages in hardened mode
   162  		if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode && c.Err.StatusCode >= 500 {
   163  			c.Err.Id = ""
   164  			c.Err.Message = "Internal Server Error"
   165  			c.Err.DetailedError = ""
   166  			c.Err.StatusCode = 500
   167  			c.Err.Where = ""
   168  			c.Err.IsOAuth = false
   169  		}
   170  
   171  		if IsApiCall(c.App, r) || IsWebhookCall(c.App, r) || len(r.Header.Get("X-Mobile-App")) > 0 {
   172  			w.WriteHeader(c.Err.StatusCode)
   173  			w.Write([]byte(c.Err.ToJson()))
   174  		} else {
   175  			utils.RenderWebAppError(c.App.Config(), w, r, c.Err, c.App.AsymmetricSigningKey())
   176  		}
   177  
   178  		if c.App.Metrics != nil {
   179  			c.App.Metrics.IncrementHttpError()
   180  		}
   181  	}
   182  
   183  	if c.App.Metrics != nil {
   184  		c.App.Metrics.IncrementHttpRequest()
   185  
   186  		if r.URL.Path != model.API_URL_SUFFIX+"/websocket" {
   187  			elapsed := float64(time.Since(now)) / float64(time.Second)
   188  			c.App.Metrics.ObserveHttpRequestDuration(elapsed)
   189  		}
   190  	}
   191  }