github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+incompatible/web/context.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 "net/http" 8 "regexp" 9 "strings" 10 11 goi18n "github.com/nicksnyder/go-i18n/i18n" 12 13 "github.com/mattermost/mattermost-server/app" 14 "github.com/mattermost/mattermost-server/mlog" 15 "github.com/mattermost/mattermost-server/model" 16 "github.com/mattermost/mattermost-server/utils" 17 ) 18 19 type Context struct { 20 App *app.App 21 Log *mlog.Logger 22 Session model.Session 23 Params *Params 24 Err *model.AppError 25 T goi18n.TranslateFunc 26 RequestId string 27 IpAddress string 28 Path string 29 siteURLHeader string 30 } 31 32 func (c *Context) LogAudit(extraInfo string) { 33 audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} 34 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 35 c.LogError(r.Err) 36 } 37 } 38 39 func (c *Context) LogAuditWithUserId(userId, extraInfo string) { 40 41 if len(c.Session.UserId) > 0 { 42 extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId) 43 } 44 45 audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} 46 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 47 c.LogError(r.Err) 48 } 49 } 50 51 func (c *Context) LogError(err *model.AppError) { 52 // Filter out 404s, endless reconnects and browser compatibility errors 53 if err.StatusCode == http.StatusNotFound || 54 (c.Path == "/api/v3/users/websocket" && err.StatusCode == http.StatusUnauthorized) || 55 err.Id == "web.check_browser_compatibility.app_error" { 56 c.LogDebug(err) 57 } else { 58 c.Log.Error( 59 err.SystemMessage(utils.TDefault), 60 mlog.String("err_where", err.Where), 61 mlog.Int("http_code", err.StatusCode), 62 mlog.String("err_details", err.DetailedError), 63 ) 64 } 65 } 66 67 func (c *Context) LogInfo(err *model.AppError) { 68 // Filter out 401s 69 if err.StatusCode == http.StatusUnauthorized { 70 c.LogDebug(err) 71 } else { 72 c.Log.Info( 73 err.SystemMessage(utils.TDefault), 74 mlog.String("err_where", err.Where), 75 mlog.Int("http_code", err.StatusCode), 76 mlog.String("err_details", err.DetailedError), 77 ) 78 } 79 } 80 81 func (c *Context) LogDebug(err *model.AppError) { 82 c.Log.Debug( 83 err.SystemMessage(utils.TDefault), 84 mlog.String("err_where", err.Where), 85 mlog.Int("http_code", err.StatusCode), 86 mlog.String("err_details", err.DetailedError), 87 ) 88 } 89 90 func (c *Context) IsSystemAdmin() bool { 91 return c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) 92 } 93 94 func (c *Context) SessionRequired() { 95 if !*c.App.Config().ServiceSettings.EnableUserAccessTokens && c.Session.Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN { 96 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserAccessToken", http.StatusUnauthorized) 97 return 98 } 99 100 if len(c.Session.UserId) == 0 { 101 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized) 102 return 103 } 104 } 105 106 func (c *Context) MfaRequired() { 107 // Must be licensed for MFA and have it configured for enforcement 108 if license := c.App.License(); license == nil || !*license.Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication { 109 return 110 } 111 112 // OAuth integrations are excepted 113 if c.Session.IsOAuth { 114 return 115 } 116 117 if user, err := c.App.GetUser(c.Session.UserId); err != nil { 118 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "MfaRequired", http.StatusUnauthorized) 119 return 120 } else { 121 // Only required for email and ldap accounts 122 if user.AuthService != "" && 123 user.AuthService != model.USER_AUTH_SERVICE_EMAIL && 124 user.AuthService != model.USER_AUTH_SERVICE_LDAP { 125 return 126 } 127 128 // Special case to let user get themself 129 if c.Path == "/api/v4/users/me" { 130 return 131 } 132 133 if !user.MfaActive { 134 c.Err = model.NewAppError("", "api.context.mfa_required.app_error", nil, "MfaRequired", http.StatusForbidden) 135 return 136 } 137 } 138 } 139 140 func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { 141 cookie := &http.Cookie{ 142 Name: model.SESSION_COOKIE_TOKEN, 143 Value: "", 144 Path: "/", 145 MaxAge: -1, 146 HttpOnly: true, 147 } 148 149 http.SetCookie(w, cookie) 150 } 151 152 func (c *Context) SetInvalidParam(parameter string) { 153 c.Err = NewInvalidParamError(parameter) 154 } 155 156 func (c *Context) SetInvalidUrlParam(parameter string) { 157 c.Err = NewInvalidUrlParamError(parameter) 158 } 159 160 func (c *Context) HandleEtag(etag string, routeName string, w http.ResponseWriter, r *http.Request) bool { 161 metrics := c.App.Metrics 162 if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 { 163 if et == etag { 164 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 165 w.WriteHeader(http.StatusNotModified) 166 if metrics != nil { 167 metrics.IncrementEtagHitCounter(routeName) 168 } 169 return true 170 } 171 } 172 173 if metrics != nil { 174 metrics.IncrementEtagMissCounter(routeName) 175 } 176 177 return false 178 } 179 180 func NewInvalidParamError(parameter string) *model.AppError { 181 err := model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]interface{}{"Name": parameter}, "", http.StatusBadRequest) 182 return err 183 } 184 func NewInvalidUrlParamError(parameter string) *model.AppError { 185 err := model.NewAppError("Context", "api.context.invalid_url_param.app_error", map[string]interface{}{"Name": parameter}, "", http.StatusBadRequest) 186 return err 187 } 188 189 func (c *Context) SetPermissionError(permission *model.Permission) { 190 c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", "+"permission="+permission.Id, http.StatusForbidden) 191 } 192 193 func (c *Context) SetSiteURLHeader(url string) { 194 c.siteURLHeader = strings.TrimRight(url, "/") 195 } 196 197 func (c *Context) GetSiteURLHeader() string { 198 return c.siteURLHeader 199 } 200 201 func (c *Context) RequireUserId() *Context { 202 if c.Err != nil { 203 return c 204 } 205 206 if c.Params.UserId == model.ME { 207 c.Params.UserId = c.Session.UserId 208 } 209 210 if len(c.Params.UserId) != 26 { 211 c.SetInvalidUrlParam("user_id") 212 } 213 return c 214 } 215 216 func (c *Context) RequireTeamId() *Context { 217 if c.Err != nil { 218 return c 219 } 220 221 if len(c.Params.TeamId) != 26 { 222 c.SetInvalidUrlParam("team_id") 223 } 224 return c 225 } 226 227 func (c *Context) RequireInviteId() *Context { 228 if c.Err != nil { 229 return c 230 } 231 232 if len(c.Params.InviteId) == 0 { 233 c.SetInvalidUrlParam("invite_id") 234 } 235 return c 236 } 237 238 func (c *Context) RequireTokenId() *Context { 239 if c.Err != nil { 240 return c 241 } 242 243 if len(c.Params.TokenId) != 26 { 244 c.SetInvalidUrlParam("token_id") 245 } 246 return c 247 } 248 249 func (c *Context) RequireChannelId() *Context { 250 if c.Err != nil { 251 return c 252 } 253 254 if len(c.Params.ChannelId) != 26 { 255 c.SetInvalidUrlParam("channel_id") 256 } 257 return c 258 } 259 260 func (c *Context) RequireUsername() *Context { 261 if c.Err != nil { 262 return c 263 } 264 265 if !model.IsValidUsername(c.Params.Username) { 266 c.SetInvalidParam("username") 267 } 268 269 return c 270 } 271 272 func (c *Context) RequirePostId() *Context { 273 if c.Err != nil { 274 return c 275 } 276 277 if len(c.Params.PostId) != 26 { 278 c.SetInvalidUrlParam("post_id") 279 } 280 return c 281 } 282 283 func (c *Context) RequireAppId() *Context { 284 if c.Err != nil { 285 return c 286 } 287 288 if len(c.Params.AppId) != 26 { 289 c.SetInvalidUrlParam("app_id") 290 } 291 return c 292 } 293 294 func (c *Context) RequireFileId() *Context { 295 if c.Err != nil { 296 return c 297 } 298 299 if len(c.Params.FileId) != 26 { 300 c.SetInvalidUrlParam("file_id") 301 } 302 303 return c 304 } 305 306 func (c *Context) RequireFilename() *Context { 307 if c.Err != nil { 308 return c 309 } 310 311 if len(c.Params.Filename) == 0 { 312 c.SetInvalidUrlParam("filename") 313 } 314 315 return c 316 } 317 318 func (c *Context) RequirePluginId() *Context { 319 if c.Err != nil { 320 return c 321 } 322 323 if len(c.Params.PluginId) == 0 { 324 c.SetInvalidUrlParam("plugin_id") 325 } 326 327 return c 328 } 329 330 func (c *Context) RequireReportId() *Context { 331 if c.Err != nil { 332 return c 333 } 334 335 if len(c.Params.ReportId) != 26 { 336 c.SetInvalidUrlParam("report_id") 337 } 338 return c 339 } 340 341 func (c *Context) RequireEmojiId() *Context { 342 if c.Err != nil { 343 return c 344 } 345 346 if len(c.Params.EmojiId) != 26 { 347 c.SetInvalidUrlParam("emoji_id") 348 } 349 return c 350 } 351 352 func (c *Context) RequireTeamName() *Context { 353 if c.Err != nil { 354 return c 355 } 356 357 if !model.IsValidTeamName(c.Params.TeamName) { 358 c.SetInvalidUrlParam("team_name") 359 } 360 361 return c 362 } 363 364 func (c *Context) RequireChannelName() *Context { 365 if c.Err != nil { 366 return c 367 } 368 369 if !model.IsValidChannelIdentifier(c.Params.ChannelName) { 370 c.SetInvalidUrlParam("channel_name") 371 } 372 373 return c 374 } 375 376 func (c *Context) RequireEmail() *Context { 377 if c.Err != nil { 378 return c 379 } 380 381 if !model.IsValidEmail(c.Params.Email) { 382 c.SetInvalidUrlParam("email") 383 } 384 385 return c 386 } 387 388 func (c *Context) RequireCategory() *Context { 389 if c.Err != nil { 390 return c 391 } 392 393 if !model.IsValidAlphaNumHyphenUnderscore(c.Params.Category, true) { 394 c.SetInvalidUrlParam("category") 395 } 396 397 return c 398 } 399 400 func (c *Context) RequireService() *Context { 401 if c.Err != nil { 402 return c 403 } 404 405 if len(c.Params.Service) == 0 { 406 c.SetInvalidUrlParam("service") 407 } 408 409 return c 410 } 411 412 func (c *Context) RequirePreferenceName() *Context { 413 if c.Err != nil { 414 return c 415 } 416 417 if !model.IsValidAlphaNumHyphenUnderscore(c.Params.PreferenceName, true) { 418 c.SetInvalidUrlParam("preference_name") 419 } 420 421 return c 422 } 423 424 func (c *Context) RequireEmojiName() *Context { 425 if c.Err != nil { 426 return c 427 } 428 429 validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`) 430 431 if len(c.Params.EmojiName) == 0 || len(c.Params.EmojiName) > model.EMOJI_NAME_MAX_LENGTH || !validName.MatchString(c.Params.EmojiName) { 432 c.SetInvalidUrlParam("emoji_name") 433 } 434 435 return c 436 } 437 438 func (c *Context) RequireHookId() *Context { 439 if c.Err != nil { 440 return c 441 } 442 443 if len(c.Params.HookId) != 26 { 444 c.SetInvalidUrlParam("hook_id") 445 } 446 447 return c 448 } 449 450 func (c *Context) RequireCommandId() *Context { 451 if c.Err != nil { 452 return c 453 } 454 455 if len(c.Params.CommandId) != 26 { 456 c.SetInvalidUrlParam("command_id") 457 } 458 return c 459 } 460 461 func (c *Context) RequireJobId() *Context { 462 if c.Err != nil { 463 return c 464 } 465 466 if len(c.Params.JobId) != 26 { 467 c.SetInvalidUrlParam("job_id") 468 } 469 return c 470 } 471 472 func (c *Context) RequireJobType() *Context { 473 if c.Err != nil { 474 return c 475 } 476 477 if len(c.Params.JobType) == 0 || len(c.Params.JobType) > 32 { 478 c.SetInvalidUrlParam("job_type") 479 } 480 return c 481 } 482 483 func (c *Context) RequireActionId() *Context { 484 if c.Err != nil { 485 return c 486 } 487 488 if len(c.Params.ActionId) != 26 { 489 c.SetInvalidUrlParam("action_id") 490 } 491 return c 492 } 493 494 func (c *Context) RequireRoleId() *Context { 495 if c.Err != nil { 496 return c 497 } 498 499 if len(c.Params.RoleId) != 26 { 500 c.SetInvalidUrlParam("role_id") 501 } 502 return c 503 } 504 505 func (c *Context) RequireSchemeId() *Context { 506 if c.Err != nil { 507 return c 508 } 509 510 if len(c.Params.SchemeId) != 26 { 511 c.SetInvalidUrlParam("scheme_id") 512 } 513 return c 514 } 515 516 func (c *Context) RequireRoleName() *Context { 517 if c.Err != nil { 518 return c 519 } 520 521 if !model.IsValidRoleName(c.Params.RoleName) { 522 c.SetInvalidUrlParam("role_name") 523 } 524 525 return c 526 }