github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/cmd/mattermost/commands/webhook.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package commands 5 6 import ( 7 "fmt" 8 "net/http" 9 "strings" 10 11 "github.com/mattermost/mattermost-server/v5/audit" 12 "github.com/mattermost/mattermost-server/v5/model" 13 "github.com/mattermost/mattermost-server/v5/store" 14 "github.com/pkg/errors" 15 "github.com/spf13/cobra" 16 ) 17 18 var WebhookCmd = &cobra.Command{ 19 Use: "webhook", 20 Short: "Management of webhooks", 21 } 22 23 var WebhookListCmd = &cobra.Command{ 24 Use: "list", 25 Short: "List webhooks", 26 Long: "list all webhooks", 27 Example: " webhook list myteam", 28 RunE: listWebhookCmdF, 29 } 30 31 var WebhookShowCmd = &cobra.Command{ 32 Use: "show [webhookId]", 33 Short: "Show a webhook", 34 Long: "Show the webhook specified by [webhookId]", 35 Args: cobra.ExactArgs(1), 36 Example: " webhook show w16zb5tu3n1zkqo18goqry1je", 37 RunE: showWebhookCmdF, 38 } 39 40 var WebhookCreateIncomingCmd = &cobra.Command{ 41 Use: "create-incoming", 42 Short: "Create incoming webhook", 43 Long: "create incoming webhook which allows external posting of messages to specific channel", 44 Example: " webhook create-incoming --channel [channelID] --user [userID] --display-name [displayName] --description [webhookDescription] --lock-to-channel --icon [iconURL]", 45 RunE: createIncomingWebhookCmdF, 46 } 47 48 var WebhookModifyIncomingCmd = &cobra.Command{ 49 Use: "modify-incoming", 50 Short: "Modify incoming webhook", 51 Long: "Modify existing incoming webhook by changing its title, description, channel or icon url", 52 Example: " webhook modify-incoming [webhookID] --channel [channelID] --display-name [displayName] --description [webhookDescription] --lock-to-channel --icon [iconURL]", 53 RunE: modifyIncomingWebhookCmdF, 54 } 55 56 var WebhookCreateOutgoingCmd = &cobra.Command{ 57 Use: "create-outgoing", 58 Short: "Create outgoing webhook", 59 Long: "create outgoing webhook which allows external posting of messages from a specific channel", 60 Example: ` webhook create-outgoing --team myteam --user myusername --display-name mywebhook --trigger-word "build" --trigger-word "test" --url http://localhost:8000/my-webhook-handler 61 webhook create-outgoing --team myteam --channel mychannel --user myusername --display-name mywebhook --description "My cool webhook" --trigger-when start --trigger-word build --trigger-word test --icon http://localhost:8000/my-slash-handler-bot-icon.png --url http://localhost:8000/my-webhook-handler --content-type "application/json"`, 62 RunE: createOutgoingWebhookCmdF, 63 } 64 65 var WebhookModifyOutgoingCmd = &cobra.Command{ 66 Use: "modify-outgoing", 67 Short: "Modify outgoing webhook", 68 Long: "Modify existing outgoing webhook by changing its title, description, channel, icon, url, content-type, and triggers", 69 Example: ` webhook modify-outgoing [webhookId] --channel [channelId] --display-name [displayName] --description "New webhook description" --icon http://localhost:8000/my-slash-handler-bot-icon.png --url http://localhost:8000/my-webhook-handler --content-type "application/json" --trigger-word test --trigger-when start`, 70 RunE: modifyOutgoingWebhookCmdF, 71 } 72 73 var WebhookDeleteCmd = &cobra.Command{ 74 Use: "delete", 75 Short: "Delete webhooks", 76 Long: "Delete webhook with given id", 77 Example: " webhook delete [webhookID]", 78 RunE: deleteWebhookCmdF, 79 } 80 81 var WebhookMoveOutgoingCmd = &cobra.Command{ 82 Use: "move-outgoing", 83 Short: "Move outgoing webhook", 84 Long: "Move outgoing webhook with an id", 85 Example: " webhook move-outgoing newteam oldteam:webhook-id --channel new-default-channel", 86 Args: cobra.ExactArgs(2), 87 RunE: moveOutgoingWebhookCmd, 88 } 89 90 func listWebhookCmdF(command *cobra.Command, args []string) error { 91 app, err := InitDBCommandContextCobra(command) 92 if err != nil { 93 return err 94 } 95 defer app.Srv().Shutdown() 96 97 var teams []*model.Team 98 if len(args) < 1 { 99 var getErr *model.AppError 100 // If no team is specified, list all teams 101 teams, getErr = app.GetAllTeams() 102 if getErr != nil { 103 return getErr 104 } 105 } else { 106 teams = getTeamsFromTeamArgs(app, args) 107 } 108 109 for i, team := range teams { 110 if team == nil { 111 CommandPrintErrorln("Unable to find team '" + args[i] + "'") 112 continue 113 } 114 115 // Fetch all hooks with a very large limit so we get them all. 116 incomingResult := make(chan store.StoreResult, 1) 117 go func() { 118 incomingHooks, err := app.Srv().Store.Webhook().GetIncomingByTeam(team.Id, 0, 100000000) 119 incomingResult <- store.StoreResult{Data: incomingHooks, NErr: err} 120 close(incomingResult) 121 }() 122 outgoingResult := make(chan store.StoreResult, 1) 123 go func() { 124 outgoingHooks, err := app.Srv().Store.Webhook().GetOutgoingByTeam(team.Id, 0, 100000000) 125 outgoingResult <- store.StoreResult{Data: outgoingHooks, NErr: err} 126 close(outgoingResult) 127 }() 128 129 if result := <-incomingResult; result.NErr == nil { 130 CommandPrettyPrintln(fmt.Sprintf("Incoming webhooks for %s (%s):", team.DisplayName, team.Name)) 131 hooks := result.Data.([]*model.IncomingWebhook) 132 for _, hook := range hooks { 133 CommandPrettyPrintln("\t" + hook.DisplayName + " (" + hook.Id + ")") 134 } 135 } else { 136 CommandPrintErrorln("Unable to list incoming webhooks for '" + args[i] + "'") 137 } 138 139 if result := <-outgoingResult; result.NErr == nil { 140 hooks := result.Data.([]*model.OutgoingWebhook) 141 CommandPrettyPrintln(fmt.Sprintf("Outgoing webhooks for %s (%s):", team.DisplayName, team.Name)) 142 for _, hook := range hooks { 143 CommandPrettyPrintln("\t" + hook.DisplayName + " (" + hook.Id + ")") 144 } 145 } else { 146 CommandPrintErrorln("Unable to list outgoing webhooks for '" + args[i] + "'") 147 } 148 } 149 return nil 150 } 151 152 func createIncomingWebhookCmdF(command *cobra.Command, args []string) error { 153 app, err := InitDBCommandContextCobra(command) 154 if err != nil { 155 return err 156 } 157 defer app.Srv().Shutdown() 158 159 channelArg, errChannel := command.Flags().GetString("channel") 160 if errChannel != nil || channelArg == "" { 161 return errors.New("Channel is required") 162 } 163 channel := getChannelFromChannelArg(app, channelArg) 164 if channel == nil { 165 return errors.New("Unable to find channel '" + channelArg + "'") 166 } 167 168 userArg, errUser := command.Flags().GetString("user") 169 if errUser != nil || userArg == "" { 170 return errors.New("User is required") 171 } 172 user := getUserFromUserArg(app, userArg) 173 if user == nil { 174 return errors.New("Unable to find user '" + userArg + "'") 175 } 176 177 displayName, _ := command.Flags().GetString("display-name") 178 description, _ := command.Flags().GetString("description") 179 iconURL, _ := command.Flags().GetString("icon") 180 channelLocked, _ := command.Flags().GetBool("lock-to-channel") 181 182 incomingWebhook := &model.IncomingWebhook{ 183 ChannelId: channel.Id, 184 DisplayName: displayName, 185 Description: description, 186 IconURL: iconURL, 187 ChannelLocked: channelLocked, 188 } 189 190 createdIncoming, errIncomingWebhook := app.CreateIncomingWebhookForChannel(user.Id, channel, incomingWebhook) 191 if errIncomingWebhook != nil { 192 return errIncomingWebhook 193 } 194 195 CommandPrettyPrintln("Id: " + createdIncoming.Id) 196 CommandPrettyPrintln("Display Name: " + createdIncoming.DisplayName) 197 198 auditRec := app.MakeAuditRecord("createIncomingWebhook", audit.Success) 199 auditRec.AddMeta("user", user) 200 auditRec.AddMeta("channel", channel) 201 auditRec.AddMeta("hook", createdIncoming) 202 app.LogAuditRec(auditRec, nil) 203 204 return nil 205 } 206 207 func modifyIncomingWebhookCmdF(command *cobra.Command, args []string) (cmdError error) { 208 app, err := InitDBCommandContextCobra(command) 209 if err != nil { 210 return err 211 } 212 defer app.Srv().Shutdown() 213 214 if len(args) < 1 { 215 return errors.New("WebhookID is not specified") 216 } 217 218 webhookArg := args[0] 219 oldHook, getErr := app.GetIncomingWebhook(webhookArg) 220 if getErr != nil { 221 return errors.New("Unable to find webhook '" + webhookArg + "'") 222 } 223 224 updatedHook := oldHook 225 226 auditRec := app.MakeAuditRecord("createIncomingWebhook", audit.Fail) 227 defer func() { app.LogAuditRec(auditRec, cmdError) }() 228 auditRec.AddMeta("hook", oldHook) 229 230 channelArg, _ := command.Flags().GetString("channel") 231 if channelArg != "" { 232 channel := getChannelFromChannelArg(app, channelArg) 233 if channel == nil { 234 return errors.New("Unable to find channel '" + channelArg + "'") 235 } 236 updatedHook.ChannelId = channel.Id 237 } 238 239 displayName, _ := command.Flags().GetString("display-name") 240 if displayName != "" { 241 updatedHook.DisplayName = displayName 242 } 243 description, _ := command.Flags().GetString("description") 244 if description != "" { 245 updatedHook.Description = description 246 } 247 iconUrl, _ := command.Flags().GetString("icon") 248 if iconUrl != "" { 249 updatedHook.IconURL = iconUrl 250 } 251 channelLocked, _ := command.Flags().GetBool("lock-to-channel") 252 updatedHook.ChannelLocked = channelLocked 253 254 updatedIncomingHook, errUpdated := app.UpdateIncomingWebhook(oldHook, updatedHook) 255 if errUpdated != nil { 256 return errUpdated 257 } 258 259 auditRec.Success() 260 auditRec.AddMeta("update", updatedIncomingHook) 261 262 return nil 263 } 264 265 func createOutgoingWebhookCmdF(command *cobra.Command, args []string) error { 266 app, err := InitDBCommandContextCobra(command) 267 if err != nil { 268 return err 269 } 270 defer app.Srv().Shutdown() 271 272 teamArg, errTeam := command.Flags().GetString("team") 273 if errTeam != nil || teamArg == "" { 274 return errors.New("Team is required") 275 } 276 team := getTeamFromTeamArg(app, teamArg) 277 if team == nil { 278 return errors.New("Unable to find team: " + teamArg) 279 } 280 281 userArg, errUser := command.Flags().GetString("user") 282 if errUser != nil || userArg == "" { 283 return errors.New("User is required") 284 } 285 user := getUserFromUserArg(app, userArg) 286 if user == nil { 287 return errors.New("Unable to find user: " + userArg) 288 } 289 290 displayName, errName := command.Flags().GetString("display-name") 291 if errName != nil || displayName == "" { 292 return errors.New("Display name is required") 293 } 294 295 triggerWords, errWords := command.Flags().GetStringArray("trigger-word") 296 if errWords != nil || len(triggerWords) == 0 { 297 return errors.New("Trigger word or words required") 298 } 299 300 callbackURLs, errURL := command.Flags().GetStringArray("url") 301 if errURL != nil || len(callbackURLs) == 0 { 302 return errors.New("Callback URL or URLs required") 303 } 304 305 triggerWhenString, _ := command.Flags().GetString("trigger-when") 306 var triggerWhen int 307 if triggerWhenString == "exact" { 308 triggerWhen = 0 309 } else if triggerWhenString == "start" { 310 triggerWhen = 1 311 } else { 312 return errors.New("Invalid trigger when parameter") 313 } 314 description, _ := command.Flags().GetString("description") 315 contentType, _ := command.Flags().GetString("content-type") 316 iconURL, _ := command.Flags().GetString("icon") 317 318 outgoingWebhook := &model.OutgoingWebhook{ 319 CreatorId: user.Id, 320 Username: user.Username, 321 TeamId: team.Id, 322 TriggerWords: triggerWords, 323 TriggerWhen: triggerWhen, 324 CallbackURLs: callbackURLs, 325 DisplayName: displayName, 326 Description: description, 327 ContentType: contentType, 328 IconURL: iconURL, 329 } 330 331 var channel *model.Channel 332 channelArg, _ := command.Flags().GetString("channel") 333 if channelArg != "" { 334 channel = getChannelFromChannelArg(app, channelArg) 335 if channel != nil { 336 outgoingWebhook.ChannelId = channel.Id 337 } 338 } 339 340 createdOutgoing, errOutgoing := app.CreateOutgoingWebhook(outgoingWebhook) 341 if errOutgoing != nil { 342 return errOutgoing 343 } 344 345 CommandPrettyPrintln("Id: " + createdOutgoing.Id) 346 CommandPrettyPrintln("Display Name: " + createdOutgoing.DisplayName) 347 348 auditRec := app.MakeAuditRecord("createOutgoingWebhook", audit.Success) 349 auditRec.AddMeta("user", user) 350 auditRec.AddMeta("hook", createdOutgoing) 351 if channel != nil { 352 auditRec.AddMeta("channel", channel) 353 } 354 app.LogAuditRec(auditRec, nil) 355 356 return nil 357 } 358 359 func modifyOutgoingWebhookCmdF(command *cobra.Command, args []string) error { 360 app, err := InitDBCommandContextCobra(command) 361 if err != nil { 362 return err 363 } 364 defer app.Srv().Shutdown() 365 366 if len(args) < 1 { 367 return errors.New("WebhookID is not specified") 368 } 369 370 webhookArg := args[0] 371 oldHook, appErr := app.GetOutgoingWebhook(webhookArg) 372 if appErr != nil { 373 return fmt.Errorf("unable to find webhook '%s'", webhookArg) 374 } 375 376 updatedHook := model.OutgoingWebhookFromJson(strings.NewReader(oldHook.ToJson())) 377 378 channelArg, _ := command.Flags().GetString("channel") 379 if channelArg != "" { 380 channel := getChannelFromChannelArg(app, channelArg) 381 if channel == nil { 382 return fmt.Errorf("unable to find channel '%s'", channelArg) 383 } 384 updatedHook.ChannelId = channel.Id 385 } 386 387 displayName, _ := command.Flags().GetString("display-name") 388 if displayName != "" { 389 updatedHook.DisplayName = displayName 390 } 391 392 description, _ := command.Flags().GetString("description") 393 if description != "" { 394 updatedHook.Description = description 395 } 396 397 triggerWords, err := command.Flags().GetStringArray("trigger-word") 398 if err != nil { 399 return errors.Wrap(err, "invalid trigger-word parameter") 400 } 401 if len(triggerWords) > 0 { 402 updatedHook.TriggerWords = triggerWords 403 } 404 405 triggerWhenString, _ := command.Flags().GetString("trigger-when") 406 if triggerWhenString != "" { 407 var triggerWhen int 408 if triggerWhenString == "exact" { 409 triggerWhen = 0 410 } else if triggerWhenString == "start" { 411 triggerWhen = 1 412 } else { 413 return errors.New("invalid trigger-when parameter") 414 } 415 updatedHook.TriggerWhen = triggerWhen 416 } 417 418 iconURL, _ := command.Flags().GetString("icon") 419 if iconURL != "" { 420 updatedHook.IconURL = iconURL 421 } 422 423 contentType, _ := command.Flags().GetString("content-type") 424 if contentType != "" { 425 updatedHook.ContentType = contentType 426 } 427 428 callbackURLs, err := command.Flags().GetStringArray("url") 429 if err != nil { 430 return errors.Wrap(err, "invalid URL parameter") 431 } 432 if len(callbackURLs) > 0 { 433 updatedHook.CallbackURLs = callbackURLs 434 } 435 436 updatedWebhook, appErr := app.UpdateOutgoingWebhook(oldHook, updatedHook) 437 if appErr != nil { 438 return appErr 439 } 440 441 auditRec := app.MakeAuditRecord("modifyOutgoingWebhook", audit.Success) 442 auditRec.AddMeta("hook", oldHook) 443 auditRec.AddMeta("update", updatedWebhook) 444 app.LogAuditRec(auditRec, nil) 445 446 return nil 447 } 448 449 func deleteWebhookCmdF(command *cobra.Command, args []string) error { 450 app, err := InitDBCommandContextCobra(command) 451 if err != nil { 452 return err 453 } 454 defer app.Srv().Shutdown() 455 456 if len(args) < 1 { 457 return errors.New("WebhookID is not specified") 458 } 459 460 webhookId := args[0] 461 errIncomingWebhook := app.DeleteIncomingWebhook(webhookId) 462 errOutgoingWebhook := app.DeleteOutgoingWebhook(webhookId) 463 464 if errIncomingWebhook != nil && errOutgoingWebhook != nil { 465 return errors.New("Unable to delete webhook '" + webhookId + "'") 466 } 467 468 auditRec := app.MakeAuditRecord("deleteWebhook", audit.Success) 469 auditRec.AddMeta("hook_id", webhookId) 470 app.LogAuditRec(auditRec, nil) 471 472 return nil 473 } 474 475 func showWebhookCmdF(command *cobra.Command, args []string) error { 476 app, err := InitDBCommandContextCobra(command) 477 if err != nil { 478 return err 479 } 480 defer app.Srv().Shutdown() 481 482 webhookId := args[0] 483 if incomingWebhook, err := app.GetIncomingWebhook(webhookId); err == nil { 484 fmt.Printf("%s", prettyPrintStruct(*incomingWebhook)) 485 return nil 486 } 487 if outgoingWebhook, err := app.GetOutgoingWebhook(webhookId); err == nil { 488 fmt.Printf("%s", prettyPrintStruct(*outgoingWebhook)) 489 return nil 490 } 491 492 return errors.New("Webhook with id " + webhookId + " not found") 493 } 494 495 func moveOutgoingWebhookCmd(command *cobra.Command, args []string) (cmdError error) { 496 app, err := InitDBCommandContextCobra(command) 497 if err != nil { 498 return err 499 } 500 defer app.Srv().Shutdown() 501 502 newTeamId := args[0] 503 _, teamError := app.GetTeam(newTeamId) 504 if teamError != nil { 505 return teamError 506 } 507 508 webhookInformation := strings.Split(args[1], ":") 509 sourceTeam := webhookInformation[0] 510 _, teamErr := app.GetTeam(sourceTeam) 511 if teamErr != nil { 512 return teamErr 513 } 514 515 webhookId := webhookInformation[1] 516 webhook, appError := app.GetOutgoingWebhook(webhookId) 517 if appError != nil { 518 return appError 519 } 520 521 auditRec := app.MakeAuditRecord("moveOutgoingWebhook", audit.Fail) 522 defer func() { app.LogAuditRec(auditRec, cmdError) }() 523 auditRec.AddMeta("hook", webhook) 524 525 channelName, channelErr := command.Flags().GetString("channel") 526 if channelErr != nil { 527 return channelErr 528 } 529 channel, getChannelErr := app.GetChannelByName(channelName, newTeamId, false) 530 531 if webhook.ChannelId != "" { 532 if getChannelErr != nil { 533 return getChannelErr 534 } 535 webhook.ChannelId = channel.Id 536 } else if channelName != "" { 537 webhook.ChannelId = channel.Id 538 } 539 540 deleteErr := app.DeleteOutgoingWebhook(webhook.Id) 541 if deleteErr != nil { 542 return deleteErr 543 } 544 545 webhook.Id = "" 546 webhook.TeamId = newTeamId 547 548 updatedWebHook, createErr := app.CreateOutgoingWebhook(webhook) 549 if createErr != nil { 550 return model.NewAppError("moveOutgoingWebhookCmd", "cli.outgoing_webhook.inconsistent_state.app_error", nil, "", http.StatusInternalServerError) 551 } 552 553 auditRec.Success() 554 auditRec.AddMeta("update", updatedWebHook) 555 556 return nil 557 } 558 559 func init() { 560 WebhookCreateIncomingCmd.Flags().String("channel", "", "Channel ID (required)") 561 WebhookCreateIncomingCmd.Flags().String("user", "", "User ID (required)") 562 WebhookCreateIncomingCmd.Flags().String("display-name", "", "Incoming webhook display name") 563 WebhookCreateIncomingCmd.Flags().String("description", "", "Incoming webhook description") 564 WebhookCreateIncomingCmd.Flags().String("icon", "", "Icon URL") 565 WebhookCreateIncomingCmd.Flags().Bool("lock-to-channel", false, "Lock to channel") 566 567 WebhookModifyIncomingCmd.Flags().String("channel", "", "Channel ID") 568 WebhookModifyIncomingCmd.Flags().String("display-name", "", "Incoming webhook display name") 569 WebhookModifyIncomingCmd.Flags().String("description", "", "Incoming webhook description") 570 WebhookModifyIncomingCmd.Flags().String("icon", "", "Icon URL") 571 WebhookModifyIncomingCmd.Flags().Bool("lock-to-channel", false, "Lock to channel") 572 573 WebhookCreateOutgoingCmd.Flags().String("team", "", "Team name or ID (required)") 574 WebhookCreateOutgoingCmd.Flags().String("channel", "", "Channel name or ID") 575 WebhookCreateOutgoingCmd.Flags().String("user", "", "User username, email, or ID (required)") 576 WebhookCreateOutgoingCmd.Flags().String("display-name", "", "Outgoing webhook display name (required)") 577 WebhookCreateOutgoingCmd.Flags().String("description", "", "Outgoing webhook description") 578 WebhookCreateOutgoingCmd.Flags().StringArray("trigger-word", []string{}, "Word to trigger webhook (required)") 579 WebhookCreateOutgoingCmd.Flags().String("trigger-when", "exact", "When to trigger webhook (exact: for first word matches a trigger word exactly, start: for first word starts with a trigger word)") 580 WebhookCreateOutgoingCmd.Flags().String("icon", "", "Icon URL") 581 WebhookCreateOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL (required)") 582 WebhookCreateOutgoingCmd.Flags().String("content-type", "", "Content-type") 583 584 WebhookModifyOutgoingCmd.Flags().String("channel", "", "Channel name or ID") 585 WebhookModifyOutgoingCmd.Flags().String("display-name", "", "Outgoing webhook display name") 586 WebhookModifyOutgoingCmd.Flags().String("description", "", "Outgoing webhook description") 587 WebhookModifyOutgoingCmd.Flags().StringArray("trigger-word", []string{}, "Word to trigger webhook") 588 WebhookModifyOutgoingCmd.Flags().String("trigger-when", "", "When to trigger webhook (exact: for first word matches a trigger word exactly, start: for first word starts with a trigger word)") 589 WebhookModifyOutgoingCmd.Flags().String("icon", "", "Icon URL") 590 WebhookModifyOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL") 591 WebhookModifyOutgoingCmd.Flags().String("content-type", "", "Content-type") 592 593 WebhookMoveOutgoingCmd.Flags().String("channel", "", "Channel name or ID") 594 595 WebhookCmd.AddCommand( 596 WebhookListCmd, 597 WebhookCreateIncomingCmd, 598 WebhookModifyIncomingCmd, 599 WebhookCreateOutgoingCmd, 600 WebhookModifyOutgoingCmd, 601 WebhookDeleteCmd, 602 WebhookShowCmd, 603 WebhookMoveOutgoingCmd, 604 ) 605 606 RootCmd.AddCommand(WebhookCmd) 607 }