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