github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/api4/webhook.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api4 5 6 import ( 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/gorilla/mux" 13 "github.com/gorilla/schema" 14 "github.com/mattermost/mattermost-server/mlog" 15 "github.com/mattermost/mattermost-server/model" 16 ) 17 18 func (api *API) InitWebhook() { 19 api.BaseRoutes.IncomingHooks.Handle("", api.ApiSessionRequired(createIncomingHook)).Methods("POST") 20 api.BaseRoutes.IncomingHooks.Handle("", api.ApiSessionRequired(getIncomingHooks)).Methods("GET") 21 api.BaseRoutes.IncomingHook.Handle("", api.ApiSessionRequired(getIncomingHook)).Methods("GET") 22 api.BaseRoutes.IncomingHook.Handle("", api.ApiSessionRequired(updateIncomingHook)).Methods("PUT") 23 api.BaseRoutes.IncomingHook.Handle("", api.ApiSessionRequired(deleteIncomingHook)).Methods("DELETE") 24 25 api.BaseRoutes.OutgoingHooks.Handle("", api.ApiSessionRequired(createOutgoingHook)).Methods("POST") 26 api.BaseRoutes.OutgoingHooks.Handle("", api.ApiSessionRequired(getOutgoingHooks)).Methods("GET") 27 api.BaseRoutes.OutgoingHook.Handle("", api.ApiSessionRequired(getOutgoingHook)).Methods("GET") 28 api.BaseRoutes.OutgoingHook.Handle("", api.ApiSessionRequired(updateOutgoingHook)).Methods("PUT") 29 api.BaseRoutes.OutgoingHook.Handle("", api.ApiSessionRequired(deleteOutgoingHook)).Methods("DELETE") 30 api.BaseRoutes.OutgoingHook.Handle("/regen_token", api.ApiSessionRequired(regenOutgoingHookToken)).Methods("POST") 31 32 api.BaseRoutes.Root.Handle("/hooks/commands/{id:[A-Za-z0-9]+}", api.ApiHandler(commandWebhook)).Methods("POST") 33 api.BaseRoutes.Root.Handle("/hooks/{id:[A-Za-z0-9]+}", api.ApiHandler(incomingWebhook)).Methods("POST") 34 35 // Old endpoint for backwards compatibility 36 api.BaseRoutes.Root.Handle("/api/v3/teams/{team_id:[A-Za-z0-9]+}/hooks/{id:[A-Za-z0-9]+}", api.ApiHandler(incomingWebhook)).Methods("POST") 37 } 38 39 func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { 40 hook := model.IncomingWebhookFromJson(r.Body) 41 if hook == nil { 42 c.SetInvalidParam("incoming_webhook") 43 return 44 } 45 46 channel, err := c.App.GetChannel(hook.ChannelId) 47 if err != nil { 48 c.Err = err 49 return 50 } 51 52 c.LogAudit("attempt") 53 54 if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 55 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 56 return 57 } 58 59 if channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 60 c.LogAudit("fail - bad channel permissions") 61 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 62 return 63 } 64 65 if incomingHook, err := c.App.CreateIncomingWebhookForChannel(c.Session.UserId, channel, hook); err != nil { 66 c.Err = err 67 return 68 } else { 69 c.LogAudit("success") 70 w.WriteHeader(http.StatusCreated) 71 w.Write([]byte(incomingHook.ToJson())) 72 } 73 } 74 75 func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { 76 c.RequireHookId() 77 if c.Err != nil { 78 return 79 } 80 81 hookId := c.Params.HookId 82 83 updatedHook := model.IncomingWebhookFromJson(r.Body) 84 if updatedHook == nil { 85 c.SetInvalidParam("incoming_webhook") 86 return 87 } 88 89 c.LogAudit("attempt") 90 91 oldHook, err := c.App.GetIncomingWebhook(hookId) 92 if err != nil { 93 c.Err = err 94 return 95 } 96 97 if updatedHook.TeamId == "" { 98 updatedHook.TeamId = oldHook.TeamId 99 } 100 101 if updatedHook.TeamId != oldHook.TeamId { 102 c.Err = model.NewAppError("updateIncomingHook", "api.webhook.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest) 103 return 104 } 105 106 if !c.App.SessionHasPermissionToTeam(c.Session, updatedHook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 107 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 108 return 109 } 110 111 if c.Session.UserId != oldHook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, updatedHook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 112 c.LogAudit("fail - inappropriate permissions") 113 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 114 return 115 } 116 117 channel, err := c.App.GetChannel(updatedHook.ChannelId) 118 if err != nil { 119 c.Err = err 120 return 121 } 122 123 if channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 124 c.LogAudit("fail - bad channel permissions") 125 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 126 return 127 } 128 129 if incomingHook, err := c.App.UpdateIncomingWebhook(oldHook, updatedHook); err != nil { 130 c.Err = err 131 return 132 } else { 133 c.LogAudit("success") 134 w.WriteHeader(http.StatusCreated) 135 w.Write([]byte(incomingHook.ToJson())) 136 } 137 } 138 139 func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { 140 teamId := r.URL.Query().Get("team_id") 141 142 var hooks []*model.IncomingWebhook 143 var err *model.AppError 144 145 if len(teamId) > 0 { 146 if !c.App.SessionHasPermissionToTeam(c.Session, teamId, model.PERMISSION_MANAGE_WEBHOOKS) { 147 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 148 return 149 } 150 151 hooks, err = c.App.GetIncomingWebhooksForTeamPage(teamId, c.Params.Page, c.Params.PerPage) 152 } else { 153 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_WEBHOOKS) { 154 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 155 return 156 } 157 158 hooks, err = c.App.GetIncomingWebhooksPage(c.Params.Page, c.Params.PerPage) 159 } 160 161 if err != nil { 162 c.Err = err 163 return 164 } 165 166 w.Write([]byte(model.IncomingWebhookListToJson(hooks))) 167 } 168 169 func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { 170 c.RequireHookId() 171 if c.Err != nil { 172 return 173 } 174 175 hookId := c.Params.HookId 176 177 var err *model.AppError 178 var hook *model.IncomingWebhook 179 var channel *model.Channel 180 181 if hook, err = c.App.GetIncomingWebhook(hookId); err != nil { 182 c.Err = err 183 return 184 } else { 185 channel, err = c.App.GetChannel(hook.ChannelId) 186 if err != nil { 187 c.Err = err 188 return 189 } 190 191 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) || 192 (channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, hook.ChannelId, model.PERMISSION_READ_CHANNEL)) { 193 c.LogAudit("fail - bad permissions") 194 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 195 return 196 } 197 198 if c.Session.UserId != hook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 199 c.LogAudit("fail - inappropriate permissions") 200 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 201 return 202 } 203 204 w.Write([]byte(hook.ToJson())) 205 return 206 } 207 } 208 209 func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { 210 c.RequireHookId() 211 if c.Err != nil { 212 return 213 } 214 215 hookId := c.Params.HookId 216 217 var err *model.AppError 218 var hook *model.IncomingWebhook 219 var channel *model.Channel 220 221 if hook, err = c.App.GetIncomingWebhook(hookId); err != nil { 222 c.Err = err 223 return 224 } else { 225 channel, err = c.App.GetChannel(hook.ChannelId) 226 if err != nil { 227 c.Err = err 228 return 229 } 230 231 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) || 232 (channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(c.Session, hook.ChannelId, model.PERMISSION_READ_CHANNEL)) { 233 c.LogAudit("fail - bad permissions") 234 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 235 return 236 } 237 238 if c.Session.UserId != hook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 239 c.LogAudit("fail - inappropriate permissions") 240 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 241 return 242 } 243 244 if err = c.App.DeleteIncomingWebhook(hookId); err != nil { 245 c.Err = err 246 return 247 } 248 249 ReturnStatusOK(w) 250 } 251 } 252 253 func updateOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { 254 c.RequireHookId() 255 if c.Err != nil { 256 return 257 } 258 259 toUpdateHook := model.OutgoingWebhookFromJson(r.Body) 260 if toUpdateHook == nil { 261 c.SetInvalidParam("outgoing_webhook") 262 return 263 } 264 265 c.LogAudit("attempt") 266 267 toUpdateHook.CreatorId = c.Session.UserId 268 269 if !c.App.SessionHasPermissionToTeam(c.Session, toUpdateHook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 270 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 271 return 272 } 273 274 oldHook, err := c.App.GetOutgoingWebhook(toUpdateHook.Id) 275 if err != nil { 276 c.Err = err 277 return 278 } 279 280 if c.Session.UserId != oldHook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, oldHook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 281 c.LogAudit("fail - inappropriate permissions") 282 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 283 return 284 } 285 286 rhook, err := c.App.UpdateOutgoingWebhook(oldHook, toUpdateHook) 287 if err != nil { 288 c.Err = err 289 return 290 } 291 292 c.LogAudit("success") 293 w.Write([]byte(rhook.ToJson())) 294 } 295 296 func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { 297 hook := model.OutgoingWebhookFromJson(r.Body) 298 if hook == nil { 299 c.SetInvalidParam("outgoing_webhook") 300 return 301 } 302 303 c.LogAudit("attempt") 304 305 hook.CreatorId = c.Session.UserId 306 307 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 308 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 309 return 310 } 311 312 if rhook, err := c.App.CreateOutgoingWebhook(hook); err != nil { 313 c.LogAudit("fail") 314 c.Err = err 315 return 316 } else { 317 c.LogAudit("success") 318 w.WriteHeader(http.StatusCreated) 319 w.Write([]byte(rhook.ToJson())) 320 } 321 } 322 323 func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { 324 channelId := r.URL.Query().Get("channel_id") 325 teamId := r.URL.Query().Get("team_id") 326 327 var hooks []*model.OutgoingWebhook 328 var err *model.AppError 329 330 if len(channelId) > 0 { 331 if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_MANAGE_WEBHOOKS) { 332 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 333 return 334 } 335 336 hooks, err = c.App.GetOutgoingWebhooksForChannelPage(channelId, c.Params.Page, c.Params.PerPage) 337 } else if len(teamId) > 0 { 338 if !c.App.SessionHasPermissionToTeam(c.Session, teamId, model.PERMISSION_MANAGE_WEBHOOKS) { 339 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 340 return 341 } 342 343 hooks, err = c.App.GetOutgoingWebhooksForTeamPage(teamId, c.Params.Page, c.Params.PerPage) 344 } else { 345 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_WEBHOOKS) { 346 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 347 return 348 } 349 350 hooks, err = c.App.GetOutgoingWebhooksPage(c.Params.Page, c.Params.PerPage) 351 } 352 353 if err != nil { 354 c.Err = err 355 return 356 } 357 358 w.Write([]byte(model.OutgoingWebhookListToJson(hooks))) 359 } 360 361 func getOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { 362 c.RequireHookId() 363 if c.Err != nil { 364 return 365 } 366 367 hook, err := c.App.GetOutgoingWebhook(c.Params.HookId) 368 if err != nil { 369 c.Err = err 370 return 371 } 372 373 c.LogAudit("attempt") 374 375 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 376 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 377 return 378 } 379 380 if c.Session.UserId != hook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 381 c.LogAudit("fail - inappropriate permissions") 382 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 383 return 384 } 385 386 c.LogAudit("success") 387 w.Write([]byte(hook.ToJson())) 388 } 389 390 func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) { 391 c.RequireHookId() 392 if c.Err != nil { 393 return 394 } 395 396 hook, err := c.App.GetOutgoingWebhook(c.Params.HookId) 397 if err != nil { 398 c.Err = err 399 return 400 } 401 402 c.LogAudit("attempt") 403 404 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 405 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 406 return 407 } 408 409 if c.Session.UserId != hook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 410 c.LogAudit("fail - inappropriate permissions") 411 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 412 return 413 } 414 415 if rhook, err := c.App.RegenOutgoingWebhookToken(hook); err != nil { 416 c.Err = err 417 return 418 } else { 419 w.Write([]byte(rhook.ToJson())) 420 } 421 } 422 423 func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { 424 c.RequireHookId() 425 if c.Err != nil { 426 return 427 } 428 429 hook, err := c.App.GetOutgoingWebhook(c.Params.HookId) 430 if err != nil { 431 c.Err = err 432 return 433 } 434 435 c.LogAudit("attempt") 436 437 if !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_WEBHOOKS) { 438 c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS) 439 return 440 } 441 442 if c.Session.UserId != hook.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { 443 c.LogAudit("fail - inappropriate permissions") 444 c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) 445 return 446 } 447 448 if err := c.App.DeleteOutgoingWebhook(hook.Id); err != nil { 449 c.LogAudit("fail") 450 c.Err = err 451 return 452 } 453 454 c.LogAudit("success") 455 ReturnStatusOK(w) 456 } 457 458 func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { 459 params := mux.Vars(r) 460 id := params["id"] 461 462 r.ParseForm() 463 464 var err *model.AppError 465 incomingWebhookPayload := &model.IncomingWebhookRequest{} 466 contentType := r.Header.Get("Content-Type") 467 if strings.Split(contentType, "; ")[0] == "application/x-www-form-urlencoded" { 468 payload := strings.NewReader(r.FormValue("payload")) 469 470 incomingWebhookPayload, err = decodePayload(payload) 471 if err != nil { 472 c.Err = err 473 return 474 } 475 } else if strings.HasPrefix(contentType, "multipart/form-data") { 476 r.ParseMultipartForm(0) 477 478 decoder := schema.NewDecoder() 479 err := decoder.Decode(incomingWebhookPayload, r.PostForm) 480 481 if err != nil { 482 c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.error", nil, err.Error(), http.StatusBadRequest) 483 return 484 } 485 } else { 486 incomingWebhookPayload, err = decodePayload(r.Body) 487 if err != nil { 488 c.Err = err 489 return 490 } 491 } 492 493 if c.App.Config().LogSettings.EnableWebhookDebugging { 494 mlog.Debug(fmt.Sprint("Incoming webhook received. Content=", incomingWebhookPayload.ToJson())) 495 } 496 497 err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload) 498 if err != nil { 499 c.Err = err 500 return 501 } 502 503 w.Header().Set("Content-Type", "text/plain") 504 w.Write([]byte("ok")) 505 } 506 507 func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) { 508 params := mux.Vars(r) 509 id := params["id"] 510 511 response, err := model.CommandResponseFromHTTPBody(r.Header.Get("Content-Type"), r.Body) 512 if err != nil { 513 c.Err = model.NewAppError("commandWebhook", "web.command_webhook.parse.app_error", nil, err.Error(), http.StatusBadRequest) 514 return 515 } 516 517 appErr := c.App.HandleCommandWebhook(id, response) 518 if appErr != nil { 519 c.Err = appErr 520 return 521 } 522 523 w.Header().Set("Content-Type", "text/plain") 524 w.Write([]byte("ok")) 525 } 526 527 func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) { 528 incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload) 529 530 if decodeError != nil { 531 return nil, decodeError 532 } 533 534 return incomingWebhookPayload, nil 535 }