github.com/bensooraj/mattermost-server@v5.11.1+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 "path" 9 "regexp" 10 "strings" 11 12 "github.com/mattermost/mattermost-server/app" 13 "github.com/mattermost/mattermost-server/mlog" 14 "github.com/mattermost/mattermost-server/model" 15 "github.com/mattermost/mattermost-server/utils" 16 ) 17 18 type Context struct { 19 App *app.App 20 Log *mlog.Logger 21 Params *Params 22 Err *model.AppError 23 siteURLHeader string 24 } 25 26 func (c *Context) LogAudit(extraInfo string) { 27 audit := &model.Audit{UserId: c.App.Session.UserId, IpAddress: c.App.IpAddress, Action: c.App.Path, ExtraInfo: extraInfo, SessionId: c.App.Session.Id} 28 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 29 c.LogError(r.Err) 30 } 31 } 32 33 func (c *Context) LogAuditWithUserId(userId, extraInfo string) { 34 35 if len(c.App.Session.UserId) > 0 { 36 extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.App.Session.UserId) 37 } 38 39 audit := &model.Audit{UserId: userId, IpAddress: c.App.IpAddress, Action: c.App.Path, ExtraInfo: extraInfo, SessionId: c.App.Session.Id} 40 if r := <-c.App.Srv.Store.Audit().Save(audit); r.Err != nil { 41 c.LogError(r.Err) 42 } 43 } 44 45 func (c *Context) LogError(err *model.AppError) { 46 // Filter out 404s, endless reconnects and browser compatibility errors 47 if err.StatusCode == http.StatusNotFound || 48 (c.App.Path == "/api/v3/users/websocket" && err.StatusCode == http.StatusUnauthorized) || 49 err.Id == "web.check_browser_compatibility.app_error" { 50 c.LogDebug(err) 51 } else { 52 c.Log.Error( 53 err.SystemMessage(utils.TDefault), 54 mlog.String("err_where", err.Where), 55 mlog.Int("http_code", err.StatusCode), 56 mlog.String("err_details", err.DetailedError), 57 ) 58 } 59 } 60 61 func (c *Context) LogInfo(err *model.AppError) { 62 // Filter out 401s 63 if err.StatusCode == http.StatusUnauthorized { 64 c.LogDebug(err) 65 } else { 66 c.Log.Info( 67 err.SystemMessage(utils.TDefault), 68 mlog.String("err_where", err.Where), 69 mlog.Int("http_code", err.StatusCode), 70 mlog.String("err_details", err.DetailedError), 71 ) 72 } 73 } 74 75 func (c *Context) LogDebug(err *model.AppError) { 76 c.Log.Debug( 77 err.SystemMessage(utils.TDefault), 78 mlog.String("err_where", err.Where), 79 mlog.Int("http_code", err.StatusCode), 80 mlog.String("err_details", err.DetailedError), 81 ) 82 } 83 84 func (c *Context) IsSystemAdmin() bool { 85 return c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) 86 } 87 88 func (c *Context) SessionRequired() { 89 if !*c.App.Config().ServiceSettings.EnableUserAccessTokens && c.App.Session.Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN { 90 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserAccessToken", http.StatusUnauthorized) 91 return 92 } 93 94 if len(c.App.Session.UserId) == 0 { 95 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized) 96 return 97 } 98 } 99 100 func (c *Context) MfaRequired() { 101 // Must be licensed for MFA and have it configured for enforcement 102 if license := c.App.License(); license == nil || !*license.Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication { 103 return 104 } 105 106 // OAuth integrations are excepted 107 if c.App.Session.IsOAuth { 108 return 109 } 110 111 if user, err := c.App.GetUser(c.App.Session.UserId); err != nil { 112 c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "MfaRequired", http.StatusUnauthorized) 113 return 114 } else { 115 // Only required for email and ldap accounts 116 if user.AuthService != "" && 117 user.AuthService != model.USER_AUTH_SERVICE_EMAIL && 118 user.AuthService != model.USER_AUTH_SERVICE_LDAP { 119 return 120 } 121 122 // Special case to let user get themself 123 subpath, _ := utils.GetSubpathFromConfig(c.App.Config()) 124 if c.App.Path == path.Join(subpath, "/api/v4/users/me") { 125 return 126 } 127 128 // Bots are exempt 129 if user.IsBot { 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 = c.App.MakePermissionError(permission) 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.App.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 } 527 528 func (c *Context) RequireGroupId() *Context { 529 if c.Err != nil { 530 return c 531 } 532 533 if len(c.Params.GroupId) != 26 { 534 c.SetInvalidUrlParam("group_id") 535 } 536 return c 537 } 538 539 func (c *Context) RequireRemoteId() *Context { 540 if c.Err != nil { 541 return c 542 } 543 544 if len(c.Params.RemoteId) == 0 { 545 c.SetInvalidUrlParam("remote_id") 546 } 547 return c 548 } 549 550 func (c *Context) RequireSyncableId() *Context { 551 if c.Err != nil { 552 return c 553 } 554 555 if len(c.Params.SyncableId) != 26 { 556 c.SetInvalidUrlParam("syncable_id") 557 } 558 return c 559 } 560 561 func (c *Context) RequireSyncableType() *Context { 562 if c.Err != nil { 563 return c 564 } 565 566 if c.Params.SyncableType != model.GroupSyncableTypeTeam && c.Params.SyncableType != model.GroupSyncableTypeChannel { 567 c.SetInvalidUrlParam("syncable_type") 568 } 569 return c 570 } 571 572 func (c *Context) RequireBotUserId() *Context { 573 if c.Err != nil { 574 return c 575 } 576 577 if len(c.Params.BotUserId) != 26 { 578 c.SetInvalidUrlParam("bot_user_id") 579 } 580 return c 581 }