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 }