github.com/jfrerich/mattermost-server@v5.8.0-rc2+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 if !user.MfaActive { 129 c.Err = model.NewAppError("", "api.context.mfa_required.app_error", nil, "MfaRequired", http.StatusForbidden) 130 return 131 } 132 } 133 } 134 135 func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { 136 cookie := &http.Cookie{ 137 Name: model.SESSION_COOKIE_TOKEN, 138 Value: "", 139 Path: "/", 140 MaxAge: -1, 141 HttpOnly: true, 142 } 143 144 http.SetCookie(w, cookie) 145 } 146 147 func (c *Context) SetInvalidParam(parameter string) { 148 c.Err = NewInvalidParamError(parameter) 149 } 150 151 func (c *Context) SetInvalidUrlParam(parameter string) { 152 c.Err = NewInvalidUrlParamError(parameter) 153 } 154 155 func (c *Context) HandleEtag(etag string, routeName string, w http.ResponseWriter, r *http.Request) bool { 156 metrics := c.App.Metrics 157 if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 { 158 if et == etag { 159 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 160 w.WriteHeader(http.StatusNotModified) 161 if metrics != nil { 162 metrics.IncrementEtagHitCounter(routeName) 163 } 164 return true 165 } 166 } 167 168 if metrics != nil { 169 metrics.IncrementEtagMissCounter(routeName) 170 } 171 172 return false 173 } 174 175 func NewInvalidParamError(parameter string) *model.AppError { 176 err := model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]interface{}{"Name": parameter}, "", http.StatusBadRequest) 177 return err 178 } 179 func NewInvalidUrlParamError(parameter string) *model.AppError { 180 err := model.NewAppError("Context", "api.context.invalid_url_param.app_error", map[string]interface{}{"Name": parameter}, "", http.StatusBadRequest) 181 return err 182 } 183 184 func (c *Context) SetPermissionError(permission *model.Permission) { 185 c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+c.App.Session.UserId+", "+"permission="+permission.Id, http.StatusForbidden) 186 } 187 188 func (c *Context) SetSiteURLHeader(url string) { 189 c.siteURLHeader = strings.TrimRight(url, "/") 190 } 191 192 func (c *Context) GetSiteURLHeader() string { 193 return c.siteURLHeader 194 } 195 196 func (c *Context) RequireUserId() *Context { 197 if c.Err != nil { 198 return c 199 } 200 201 if c.Params.UserId == model.ME { 202 c.Params.UserId = c.App.Session.UserId 203 } 204 205 if len(c.Params.UserId) != 26 { 206 c.SetInvalidUrlParam("user_id") 207 } 208 return c 209 } 210 211 func (c *Context) RequireTeamId() *Context { 212 if c.Err != nil { 213 return c 214 } 215 216 if len(c.Params.TeamId) != 26 { 217 c.SetInvalidUrlParam("team_id") 218 } 219 return c 220 } 221 222 func (c *Context) RequireInviteId() *Context { 223 if c.Err != nil { 224 return c 225 } 226 227 if len(c.Params.InviteId) == 0 { 228 c.SetInvalidUrlParam("invite_id") 229 } 230 return c 231 } 232 233 func (c *Context) RequireTokenId() *Context { 234 if c.Err != nil { 235 return c 236 } 237 238 if len(c.Params.TokenId) != 26 { 239 c.SetInvalidUrlParam("token_id") 240 } 241 return c 242 } 243 244 func (c *Context) RequireChannelId() *Context { 245 if c.Err != nil { 246 return c 247 } 248 249 if len(c.Params.ChannelId) != 26 { 250 c.SetInvalidUrlParam("channel_id") 251 } 252 return c 253 } 254 255 func (c *Context) RequireUsername() *Context { 256 if c.Err != nil { 257 return c 258 } 259 260 if !model.IsValidUsername(c.Params.Username) { 261 c.SetInvalidParam("username") 262 } 263 264 return c 265 } 266 267 func (c *Context) RequirePostId() *Context { 268 if c.Err != nil { 269 return c 270 } 271 272 if len(c.Params.PostId) != 26 { 273 c.SetInvalidUrlParam("post_id") 274 } 275 return c 276 } 277 278 func (c *Context) RequireAppId() *Context { 279 if c.Err != nil { 280 return c 281 } 282 283 if len(c.Params.AppId) != 26 { 284 c.SetInvalidUrlParam("app_id") 285 } 286 return c 287 } 288 289 func (c *Context) RequireFileId() *Context { 290 if c.Err != nil { 291 return c 292 } 293 294 if len(c.Params.FileId) != 26 { 295 c.SetInvalidUrlParam("file_id") 296 } 297 298 return c 299 } 300 301 func (c *Context) RequireFilename() *Context { 302 if c.Err != nil { 303 return c 304 } 305 306 if len(c.Params.Filename) == 0 { 307 c.SetInvalidUrlParam("filename") 308 } 309 310 return c 311 } 312 313 func (c *Context) RequirePluginId() *Context { 314 if c.Err != nil { 315 return c 316 } 317 318 if len(c.Params.PluginId) == 0 { 319 c.SetInvalidUrlParam("plugin_id") 320 } 321 322 return c 323 } 324 325 func (c *Context) RequireReportId() *Context { 326 if c.Err != nil { 327 return c 328 } 329 330 if len(c.Params.ReportId) != 26 { 331 c.SetInvalidUrlParam("report_id") 332 } 333 return c 334 } 335 336 func (c *Context) RequireEmojiId() *Context { 337 if c.Err != nil { 338 return c 339 } 340 341 if len(c.Params.EmojiId) != 26 { 342 c.SetInvalidUrlParam("emoji_id") 343 } 344 return c 345 } 346 347 func (c *Context) RequireTeamName() *Context { 348 if c.Err != nil { 349 return c 350 } 351 352 if !model.IsValidTeamName(c.Params.TeamName) { 353 c.SetInvalidUrlParam("team_name") 354 } 355 356 return c 357 } 358 359 func (c *Context) RequireChannelName() *Context { 360 if c.Err != nil { 361 return c 362 } 363 364 if !model.IsValidChannelIdentifier(c.Params.ChannelName) { 365 c.SetInvalidUrlParam("channel_name") 366 } 367 368 return c 369 } 370 371 func (c *Context) RequireEmail() *Context { 372 if c.Err != nil { 373 return c 374 } 375 376 if !model.IsValidEmail(c.Params.Email) { 377 c.SetInvalidUrlParam("email") 378 } 379 380 return c 381 } 382 383 func (c *Context) RequireCategory() *Context { 384 if c.Err != nil { 385 return c 386 } 387 388 if !model.IsValidAlphaNumHyphenUnderscore(c.Params.Category, true) { 389 c.SetInvalidUrlParam("category") 390 } 391 392 return c 393 } 394 395 func (c *Context) RequireService() *Context { 396 if c.Err != nil { 397 return c 398 } 399 400 if len(c.Params.Service) == 0 { 401 c.SetInvalidUrlParam("service") 402 } 403 404 return c 405 } 406 407 func (c *Context) RequirePreferenceName() *Context { 408 if c.Err != nil { 409 return c 410 } 411 412 if !model.IsValidAlphaNumHyphenUnderscore(c.Params.PreferenceName, true) { 413 c.SetInvalidUrlParam("preference_name") 414 } 415 416 return c 417 } 418 419 func (c *Context) RequireEmojiName() *Context { 420 if c.Err != nil { 421 return c 422 } 423 424 validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`) 425 426 if len(c.Params.EmojiName) == 0 || len(c.Params.EmojiName) > model.EMOJI_NAME_MAX_LENGTH || !validName.MatchString(c.Params.EmojiName) { 427 c.SetInvalidUrlParam("emoji_name") 428 } 429 430 return c 431 } 432 433 func (c *Context) RequireHookId() *Context { 434 if c.Err != nil { 435 return c 436 } 437 438 if len(c.Params.HookId) != 26 { 439 c.SetInvalidUrlParam("hook_id") 440 } 441 442 return c 443 } 444 445 func (c *Context) RequireCommandId() *Context { 446 if c.Err != nil { 447 return c 448 } 449 450 if len(c.Params.CommandId) != 26 { 451 c.SetInvalidUrlParam("command_id") 452 } 453 return c 454 } 455 456 func (c *Context) RequireJobId() *Context { 457 if c.Err != nil { 458 return c 459 } 460 461 if len(c.Params.JobId) != 26 { 462 c.SetInvalidUrlParam("job_id") 463 } 464 return c 465 } 466 467 func (c *Context) RequireJobType() *Context { 468 if c.Err != nil { 469 return c 470 } 471 472 if len(c.Params.JobType) == 0 || len(c.Params.JobType) > 32 { 473 c.SetInvalidUrlParam("job_type") 474 } 475 return c 476 } 477 478 func (c *Context) RequireActionId() *Context { 479 if c.Err != nil { 480 return c 481 } 482 483 if len(c.Params.ActionId) != 26 { 484 c.SetInvalidUrlParam("action_id") 485 } 486 return c 487 } 488 489 func (c *Context) RequireRoleId() *Context { 490 if c.Err != nil { 491 return c 492 } 493 494 if len(c.Params.RoleId) != 26 { 495 c.SetInvalidUrlParam("role_id") 496 } 497 return c 498 } 499 500 func (c *Context) RequireSchemeId() *Context { 501 if c.Err != nil { 502 return c 503 } 504 505 if len(c.Params.SchemeId) != 26 { 506 c.SetInvalidUrlParam("scheme_id") 507 } 508 return c 509 } 510 511 func (c *Context) RequireRoleName() *Context { 512 if c.Err != nil { 513 return c 514 } 515 516 if !model.IsValidRoleName(c.Params.RoleName) { 517 c.SetInvalidUrlParam("role_name") 518 } 519 520 return c 521 } 522 523 func (c *Context) RequireGroupId() *Context { 524 if c.Err != nil { 525 return c 526 } 527 528 if len(c.Params.GroupId) != 26 { 529 c.SetInvalidUrlParam("group_id") 530 } 531 return c 532 } 533 534 func (c *Context) RequireRemoteId() *Context { 535 if c.Err != nil { 536 return c 537 } 538 539 if len(c.Params.RemoteId) == 0 { 540 c.SetInvalidUrlParam("remote_id") 541 } 542 return c 543 } 544 545 func (c *Context) RequireSyncableId() *Context { 546 if c.Err != nil { 547 return c 548 } 549 550 if len(c.Params.SyncableId) != 26 { 551 c.SetInvalidUrlParam("syncable_id") 552 } 553 return c 554 } 555 556 func (c *Context) RequireSyncableType() *Context { 557 if c.Err != nil { 558 return c 559 } 560 561 if c.Params.SyncableType != model.GroupSyncableTypeTeam && c.Params.SyncableType != model.GroupSyncableTypeChannel { 562 c.SetInvalidUrlParam("syncable_type") 563 } 564 return c 565 }