github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+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 App: w.App, 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 App: w.App, 31 HandleFunc: h, 32 RequireSession: false, 33 TrustRequester: false, 34 RequireMfa: false, 35 IsStatic: true, 36 } 37 } 38 39 type Handler struct { 40 App *app.App 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 = h.App 54 c.T, _ = utils.GetTranslationsAndLocale(w, r) 55 c.RequestId = model.NewId() 56 c.IpAddress = utils.GetIpAddress(r) 57 c.Params = ParamsFromRequest(r) 58 c.Path = r.URL.Path 59 c.Log = c.App.Log 60 61 token, tokenLocation := app.ParseAuthTokenFromRequest(r) 62 63 // CSRF Check 64 if tokenLocation == app.TokenLocationCookie && h.RequireSession && !h.TrustRequester { 65 if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML { 66 c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized) 67 token = "" 68 } 69 } 70 71 c.SetSiteURLHeader(app.GetProtocol(r) + "://" + r.Host) 72 73 w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) 74 w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.License() != nil)) 75 76 if h.IsStatic { 77 // Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking 78 w.Header().Set("X-Frame-Options", "SAMEORIGIN") 79 w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'") 80 } else { 81 // All api response bodies will be JSON formatted by default 82 w.Header().Set("Content-Type", "application/json") 83 84 if r.Method == "GET" { 85 w.Header().Set("Expires", "0") 86 } 87 } 88 89 if len(token) != 0 { 90 session, err := c.App.GetSession(token) 91 92 if err != nil { 93 c.Log.Info("Invalid session", mlog.Err(err)) 94 if err.StatusCode == http.StatusInternalServerError { 95 c.Err = err 96 } else if h.RequireSession { 97 c.RemoveSessionCookie(w, r) 98 c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) 99 } 100 } else if !session.IsOAuth && tokenLocation == app.TokenLocationQueryString { 101 c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized) 102 } else { 103 c.Session = *session 104 } 105 106 // Rate limit by UserID 107 if c.App.Srv.RateLimiter != nil && c.App.Srv.RateLimiter.UserIdRateLimit(c.Session.UserId, w) { 108 return 109 } 110 } 111 112 c.Log = c.App.Log.With( 113 mlog.String("path", c.Path), 114 mlog.String("request_id", c.RequestId), 115 mlog.String("ip_addr", c.IpAddress), 116 mlog.String("user_id", c.Session.UserId), 117 mlog.String("method", r.Method), 118 ) 119 120 if c.Err == nil && h.RequireSession { 121 c.SessionRequired() 122 } 123 124 if c.Err == nil && h.RequireMfa { 125 c.MfaRequired() 126 } 127 128 if c.Err == nil { 129 h.HandleFunc(c, w, r) 130 } 131 132 // Handle errors that have occurred 133 if c.Err != nil { 134 c.Err.Translate(c.T) 135 c.Err.RequestId = c.RequestId 136 137 if c.Err.Id == "api.context.session_expired.app_error" { 138 c.LogInfo(c.Err) 139 } else { 140 c.LogError(c.Err) 141 } 142 143 c.Err.Where = r.URL.Path 144 145 // Block out detailed error when not in developer mode 146 if !*c.App.Config().ServiceSettings.EnableDeveloper { 147 c.Err.DetailedError = "" 148 } 149 150 // Sanitize all 5xx error messages in hardened mode 151 if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode && c.Err.StatusCode >= 500 { 152 c.Err.Id = "" 153 c.Err.Message = "Internal Server Error" 154 c.Err.DetailedError = "" 155 c.Err.StatusCode = 500 156 c.Err.Where = "" 157 c.Err.IsOAuth = false 158 } 159 160 w.WriteHeader(c.Err.StatusCode) 161 w.Write([]byte(c.Err.ToJson())) 162 163 if c.App.Metrics != nil { 164 c.App.Metrics.IncrementHttpError() 165 } 166 } 167 168 if c.App.Metrics != nil { 169 c.App.Metrics.IncrementHttpRequest() 170 171 if r.URL.Path != model.API_URL_SUFFIX+"/websocket" { 172 elapsed := float64(time.Since(now)) / float64(time.Second) 173 c.App.Metrics.ObserveHttpRequestDuration(elapsed) 174 } 175 } 176 }