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