github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/api/context.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 "strings" 11 "sync/atomic" 12 "time" 13 14 l4g "github.com/alecthomas/log4go" 15 "github.com/gorilla/mux" 16 goi18n "github.com/nicksnyder/go-i18n/i18n" 17 18 "github.com/mattermost/mattermost-server/app" 19 "github.com/mattermost/mattermost-server/model" 20 "github.com/mattermost/mattermost-server/utils" 21 ) 22 23 type Context struct { 24 App *app.App 25 Session model.Session 26 RequestId string 27 IpAddress string 28 Path string 29 Err *model.AppError 30 siteURLHeader string 31 teamURLValid bool 32 teamURL string 33 T goi18n.TranslateFunc 34 Locale string 35 TeamId string 36 } 37 38 func (api *API) ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 39 return &handler{api.App, h, false, false, true, false, false, false, false} 40 } 41 42 func (api *API) AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 43 return &handler{api.App, h, false, false, false, false, false, false, false} 44 } 45 46 func (api *API) AppHandlerIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 47 return &handler{api.App, h, false, false, false, false, true, false, false} 48 } 49 50 func (api *API) ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 51 return &handler{api.App, h, true, false, true, false, false, false, true} 52 } 53 54 func (api *API) ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler { 55 return &handler{api.App, h, true, false, true, isUserActivity, false, false, true} 56 } 57 58 func (api *API) ApiUserRequiredMfa(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 59 return &handler{api.App, h, true, false, true, false, false, false, false} 60 } 61 62 func (api *API) UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 63 return &handler{api.App, h, true, false, false, false, false, false, true} 64 } 65 66 func (api *API) AppHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 67 return &handler{api.App, h, false, false, false, false, false, true, false} 68 } 69 70 func (api *API) ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 71 return &handler{api.App, h, true, true, true, false, false, false, true} 72 } 73 74 func (api *API) ApiAdminSystemRequiredTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 75 return &handler{api.App, h, true, true, true, false, false, true, true} 76 } 77 78 func (api *API) ApiAppHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 79 return &handler{api.App, h, false, false, true, false, false, true, false} 80 } 81 82 func (api *API) ApiUserRequiredTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 83 return &handler{api.App, h, true, false, true, false, false, true, true} 84 } 85 86 func (api *API) ApiAppHandlerTrustRequesterIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 87 return &handler{api.App, h, false, false, true, false, true, true, false} 88 } 89 90 type handler struct { 91 app *app.App 92 handleFunc func(*Context, http.ResponseWriter, *http.Request) 93 requireUser bool 94 requireSystemAdmin bool 95 isApi bool 96 isUserActivity bool 97 isTeamIndependent bool 98 trustRequester bool 99 requireMfa bool 100 } 101 102 func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 103 now := time.Now() 104 l4g.Debug("%v", r.URL.Path) 105 106 c := &Context{} 107 c.App = h.app 108 c.T, c.Locale = utils.GetTranslationsAndLocale(w, r) 109 c.RequestId = model.NewId() 110 c.IpAddress = utils.GetIpAddress(r) 111 c.TeamId = mux.Vars(r)["team_id"] 112 113 if metrics := c.App.Metrics; metrics != nil && h.isApi { 114 metrics.IncrementHttpRequest() 115 } 116 117 token := "" 118 isTokenFromQueryString := false 119 120 // Attempt to parse token out of the header 121 authHeader := r.Header.Get(model.HEADER_AUTH) 122 if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER { 123 // Default session token 124 token = authHeader[7:] 125 126 } else if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN { 127 // OAuth token 128 token = authHeader[6:] 129 } 130 131 // Attempt to parse the token from the cookie 132 if len(token) == 0 { 133 if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil { 134 token = cookie.Value 135 136 if (h.requireSystemAdmin || h.requireUser) && !h.trustRequester { 137 if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML { 138 c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized) 139 token = "" 140 } 141 } 142 } 143 } 144 145 // Attempt to parse token out of the query string 146 if len(token) == 0 { 147 token = r.URL.Query().Get("access_token") 148 isTokenFromQueryString = true 149 } 150 151 c.SetSiteURLHeader(app.GetProtocol(r) + "://" + r.Host) 152 153 w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) 154 w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), utils.IsLicensed())) 155 156 // Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking 157 if !h.isApi { 158 w.Header().Set("X-Frame-Options", "SAMEORIGIN") 159 w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'") 160 } else { 161 // All api response bodies will be JSON formatted by default 162 w.Header().Set("Content-Type", "application/json") 163 164 if r.Method == "GET" { 165 w.Header().Set("Expires", "0") 166 } 167 } 168 169 if len(token) != 0 { 170 session, err := c.App.GetSession(token) 171 172 if err != nil { 173 l4g.Error(utils.T("api.context.invalid_session.error"), err.Error()) 174 c.RemoveSessionCookie(w, r) 175 if h.requireUser || h.requireSystemAdmin { 176 c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) 177 } 178 } else if !session.IsOAuth && isTokenFromQueryString { 179 c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized) 180 } else { 181 c.Session = *session 182 } 183 } 184 185 if h.isApi || h.isTeamIndependent { 186 c.setTeamURL(c.GetSiteURLHeader(), false) 187 c.Path = r.URL.Path 188 } else { 189 splitURL := strings.Split(r.URL.Path, "/") 190 c.setTeamURL(c.GetSiteURLHeader()+"/"+splitURL[1], true) 191 c.Path = "/" + strings.Join(splitURL[2:], "/") 192 } 193 194 if h.isApi && !*c.App.Config().ServiceSettings.EnableAPIv3 { 195 c.Err = model.NewAppError("ServeHTTP", "api.context.v3_disabled.app_error", nil, "", http.StatusNotImplemented) 196 } 197 198 if c.Err == nil && h.requireUser { 199 c.UserRequired() 200 } 201 202 if c.Err == nil && h.requireMfa { 203 c.MfaRequired() 204 } 205 206 if c.Err == nil && h.requireSystemAdmin { 207 c.SystemAdminRequired() 208 } 209 210 if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 { 211 c.App.SetStatusOnline(c.Session.UserId, c.Session.Id, false) 212 c.App.UpdateLastActivityAtIfNeeded(c.Session) 213 } 214 215 if c.Err == nil && (h.requireUser || h.requireSystemAdmin) { 216 //check if teamId exist 217 c.CheckTeamId() 218 } 219 220 if h.isApi { 221 atomic.StoreInt32(model.UsedApiV3, 1) 222 } 223 224 if c.Err == nil { 225 h.handleFunc(c, w, r) 226 } 227 228 // Handle errors that have occoured 229 if c.Err != nil { 230 c.Err.Translate(c.T) 231 c.Err.RequestId = c.RequestId 232 c.LogError(c.Err) 233 c.Err.Where = r.URL.Path 234 235 // Block out detailed error when not in developer mode 236 if !*c.App.Config().ServiceSettings.EnableDeveloper { 237 c.Err.DetailedError = "" 238 } 239 240 if h.isApi { 241 w.WriteHeader(c.Err.StatusCode) 242 w.Write([]byte(c.Err.ToJson())) 243 244 if c.App.Metrics != nil { 245 c.App.Metrics.IncrementHttpError() 246 } 247 } else { 248 if c.Err.StatusCode == http.StatusUnauthorized { 249 http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) 250 } else { 251 utils.RenderWebError(c.Err, w, r) 252 } 253 } 254 255 } 256 257 if h.isApi && c.App.Metrics != nil { 258 if r.URL.Path != model.API_URL_SUFFIX_V3+"/users/websocket" { 259 elapsed := float64(time.Since(now)) / float64(time.Second) 260 c.App.Metrics.ObserveHttpRequestDuration(elapsed) 261 } 262 } 263 } 264 265 func (c *Context) LogAudit(extraInfo string) { 266 audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} 267 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 268 c.LogError(r.Err) 269 } 270 } 271 272 func (c *Context) LogAuditWithUserId(userId, extraInfo string) { 273 274 if len(c.Session.UserId) > 0 { 275 extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId) 276 } 277 278 audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} 279 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 280 c.LogError(r.Err) 281 } 282 } 283 284 func (c *Context) LogError(err *model.AppError) { 285 286 // filter out endless reconnects 287 if c.Path == "/api/v3/users/websocket" && err.StatusCode == 401 || err.Id == "web.check_browser_compatibility.app_error" { 288 c.LogDebug(err) 289 } else if err.Id != "api.post.create_post.town_square_read_only" { 290 l4g.Error(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode, 291 c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError) 292 } 293 } 294 295 func (c *Context) LogDebug(err *model.AppError) { 296 l4g.Debug(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode, 297 c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError) 298 } 299 300 func (c *Context) UserRequired() { 301 if !*c.App.Config().ServiceSettings.EnableUserAccessTokens && c.Session.Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN { 302 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserAccessToken", http.StatusUnauthorized) 303 return 304 } 305 306 if len(c.Session.UserId) == 0 { 307 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized) 308 return 309 } 310 } 311 312 func (c *Context) MfaRequired() { 313 // Must be licensed for MFA and have it configured for enforcement 314 if !utils.IsLicensed() || !*utils.License().Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication { 315 return 316 } 317 318 // OAuth integrations are excepted 319 if c.Session.IsOAuth { 320 return 321 } 322 323 if result := <-c.App.Srv.Store.User().Get(c.Session.UserId); result.Err != nil { 324 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "MfaRequired", http.StatusUnauthorized) 325 return 326 } else { 327 user := result.Data.(*model.User) 328 329 // Only required for email and ldap accounts 330 if user.AuthService != "" && 331 user.AuthService != model.USER_AUTH_SERVICE_EMAIL && 332 user.AuthService != model.USER_AUTH_SERVICE_LDAP { 333 return 334 } 335 336 if !user.MfaActive { 337 c.Err = model.NewAppError("", "api.context.mfa_required.app_error", nil, "MfaRequired", http.StatusUnauthorized) 338 return 339 } 340 } 341 } 342 343 func (c *Context) SystemAdminRequired() { 344 if len(c.Session.UserId) == 0 { 345 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "SystemAdminRequired", http.StatusUnauthorized) 346 return 347 } else if !c.IsSystemAdmin() { 348 c.Err = model.NewAppError("", "api.context.permissions.app_error", nil, "AdminRequired", http.StatusForbidden) 349 return 350 } 351 } 352 353 func (c *Context) IsSystemAdmin() bool { 354 return c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) 355 } 356 357 func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { 358 cookie := &http.Cookie{ 359 Name: model.SESSION_COOKIE_TOKEN, 360 Value: "", 361 Path: "/", 362 MaxAge: -1, 363 HttpOnly: true, 364 } 365 366 userCookie := &http.Cookie{ 367 Name: model.SESSION_COOKIE_USER, 368 Value: "", 369 Path: "/", 370 MaxAge: -1, 371 } 372 373 http.SetCookie(w, cookie) 374 http.SetCookie(w, userCookie) 375 } 376 377 func (c *Context) SetInvalidParam(where string, name string) { 378 c.Err = NewInvalidParamError(where, name) 379 } 380 381 func NewInvalidParamError(where string, name string) *model.AppError { 382 err := model.NewAppError(where, "api.context.invalid_param.app_error", map[string]interface{}{"Name": name}, "", http.StatusBadRequest) 383 return err 384 } 385 386 func (c *Context) SetUnknownError(where string, details string) { 387 c.Err = model.NewAppError(where, "api.context.unknown.app_error", nil, details, http.StatusInternalServerError) 388 } 389 390 func (c *Context) SetPermissionError(permission *model.Permission) { 391 c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", "+"permission="+permission.Id, http.StatusForbidden) 392 } 393 394 func (c *Context) setTeamURL(url string, valid bool) { 395 c.teamURL = url 396 c.teamURLValid = valid 397 } 398 399 func (c *Context) SetTeamURLFromSession() { 400 if result := <-c.App.Srv.Store.Team().Get(c.TeamId); result.Err == nil { 401 c.setTeamURL(c.GetSiteURLHeader()+"/"+result.Data.(*model.Team).Name, true) 402 } 403 } 404 405 func (c *Context) SetSiteURLHeader(url string) { 406 c.siteURLHeader = strings.TrimRight(url, "/") 407 } 408 409 // TODO see where these are used 410 func (c *Context) GetTeamURLFromTeam(team *model.Team) string { 411 return c.GetSiteURLHeader() + "/" + team.Name 412 } 413 414 func (c *Context) GetTeamURL() string { 415 if !c.teamURLValid { 416 c.SetTeamURLFromSession() 417 if !c.teamURLValid { 418 l4g.Debug(utils.T("api.context.invalid_team_url.debug")) 419 } 420 } 421 return c.teamURL 422 } 423 424 func (c *Context) GetSiteURLHeader() string { 425 return c.siteURLHeader 426 } 427 428 func (c *Context) GetCurrentTeamMember() *model.TeamMember { 429 return c.Session.GetTeamByTeamId(c.TeamId) 430 } 431 432 func (c *Context) HandleEtag(etag string, routeName string, w http.ResponseWriter, r *http.Request) bool { 433 metrics := c.App.Metrics 434 if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 { 435 if et == etag { 436 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 437 w.WriteHeader(http.StatusNotModified) 438 if metrics != nil { 439 metrics.IncrementEtagHitCounter(routeName) 440 } 441 return true 442 } 443 } 444 445 if metrics != nil { 446 metrics.IncrementEtagMissCounter(routeName) 447 } 448 449 return false 450 } 451 452 func IsApiCall(r *http.Request) bool { 453 return strings.Index(r.URL.Path, "/api/") == 0 454 } 455 456 func Handle404(w http.ResponseWriter, r *http.Request) { 457 err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound) 458 459 l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)) 460 461 if IsApiCall(r) { 462 w.WriteHeader(err.StatusCode) 463 err.DetailedError = "There doesn't appear to be an api call for the url='" + r.URL.Path + "'. Typo? are you missing a team_id or user_id as part of the url?" 464 w.Write([]byte(err.ToJson())) 465 } else { 466 utils.RenderWebError(err, w, r) 467 } 468 } 469 470 func (c *Context) CheckTeamId() { 471 if c.TeamId != "" && c.Session.GetTeamByTeamId(c.TeamId) == nil { 472 if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 473 if result := <-c.App.Srv.Store.Team().Get(c.TeamId); result.Err != nil { 474 c.Err = result.Err 475 c.Err.StatusCode = http.StatusBadRequest 476 return 477 } 478 } else { 479 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 480 return 481 } 482 } 483 }