github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/command.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/masterhung0112/hk_server/v5/audit"
    12  	"github.com/masterhung0112/hk_server/v5/model"
    13  )
    14  
    15  func (api *API) InitCommand() {
    16  	api.BaseRoutes.Commands.Handle("", api.ApiSessionRequired(createCommand)).Methods("POST")
    17  	api.BaseRoutes.Commands.Handle("", api.ApiSessionRequired(listCommands)).Methods("GET")
    18  	api.BaseRoutes.Commands.Handle("/execute", api.ApiSessionRequired(executeCommand)).Methods("POST")
    19  
    20  	api.BaseRoutes.Command.Handle("", api.ApiSessionRequired(getCommand)).Methods("GET")
    21  	api.BaseRoutes.Command.Handle("", api.ApiSessionRequired(updateCommand)).Methods("PUT")
    22  	api.BaseRoutes.Command.Handle("/move", api.ApiSessionRequired(moveCommand)).Methods("PUT")
    23  	api.BaseRoutes.Command.Handle("", api.ApiSessionRequired(deleteCommand)).Methods("DELETE")
    24  
    25  	api.BaseRoutes.Team.Handle("/commands/autocomplete", api.ApiSessionRequired(listAutocompleteCommands)).Methods("GET")
    26  	api.BaseRoutes.Team.Handle("/commands/autocomplete_suggestions", api.ApiSessionRequired(listCommandAutocompleteSuggestions)).Methods("GET")
    27  	api.BaseRoutes.Command.Handle("/regen_token", api.ApiSessionRequired(regenCommandToken)).Methods("PUT")
    28  }
    29  
    30  func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
    31  	cmd := model.CommandFromJson(r.Body)
    32  	if cmd == nil {
    33  		c.SetInvalidParam("command")
    34  		return
    35  	}
    36  
    37  	auditRec := c.MakeAuditRecord("createCommand", audit.Fail)
    38  	defer c.LogAuditRec(auditRec)
    39  	c.LogAudit("attempt")
    40  
    41  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
    42  		c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
    43  		return
    44  	}
    45  
    46  	cmd.CreatorId = c.AppContext.Session().UserId
    47  
    48  	rcmd, err := c.App.CreateCommand(cmd)
    49  	if err != nil {
    50  		c.Err = err
    51  		return
    52  	}
    53  
    54  	auditRec.Success()
    55  	c.LogAudit("success")
    56  	auditRec.AddMeta("command", rcmd)
    57  
    58  	w.WriteHeader(http.StatusCreated)
    59  	w.Write([]byte(rcmd.ToJson()))
    60  }
    61  
    62  func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
    63  	c.RequireCommandId()
    64  	if c.Err != nil {
    65  		return
    66  	}
    67  
    68  	cmd := model.CommandFromJson(r.Body)
    69  	if cmd == nil || cmd.Id != c.Params.CommandId {
    70  		c.SetInvalidParam("command")
    71  		return
    72  	}
    73  
    74  	auditRec := c.MakeAuditRecord("updateCommand", audit.Fail)
    75  	defer c.LogAuditRec(auditRec)
    76  	c.LogAudit("attempt")
    77  
    78  	oldCmd, err := c.App.GetCommand(c.Params.CommandId)
    79  	if err != nil {
    80  		auditRec.AddMeta("command_id", c.Params.CommandId)
    81  		c.SetCommandNotFoundError()
    82  		return
    83  	}
    84  	auditRec.AddMeta("command", oldCmd)
    85  
    86  	if cmd.TeamId != oldCmd.TeamId {
    87  		c.Err = model.NewAppError("updateCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.AppContext.Session().UserId, http.StatusBadRequest)
    88  		return
    89  	}
    90  
    91  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), oldCmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
    92  		c.LogAudit("fail - inappropriate permissions")
    93  		// here we return Not_found instead of a permissions error so we don't leak the existence of
    94  		// a command to someone without permissions for the team it belongs to.
    95  		c.SetCommandNotFoundError()
    96  		return
    97  	}
    98  
    99  	if c.AppContext.Session().UserId != oldCmd.CreatorId && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), oldCmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
   100  		c.LogAudit("fail - inappropriate permissions")
   101  		c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
   102  		return
   103  	}
   104  
   105  	rcmd, err := c.App.UpdateCommand(oldCmd, cmd)
   106  	if err != nil {
   107  		c.Err = err
   108  		return
   109  	}
   110  
   111  	auditRec.Success()
   112  	c.LogAudit("success")
   113  
   114  	w.Write([]byte(rcmd.ToJson()))
   115  }
   116  
   117  func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
   118  	c.RequireCommandId()
   119  	if c.Err != nil {
   120  		return
   121  	}
   122  
   123  	cmr, err := model.CommandMoveRequestFromJson(r.Body)
   124  	if err != nil {
   125  		c.SetInvalidParam("team_id")
   126  		return
   127  	}
   128  
   129  	auditRec := c.MakeAuditRecord("moveCommand", audit.Fail)
   130  	defer c.LogAuditRec(auditRec)
   131  	c.LogAudit("attempt")
   132  
   133  	newTeam, appErr := c.App.GetTeam(cmr.TeamId)
   134  	if appErr != nil {
   135  		c.Err = appErr
   136  		return
   137  	}
   138  	auditRec.AddMeta("team", newTeam)
   139  
   140  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), newTeam.Id, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   141  		c.LogAudit("fail - inappropriate permissions")
   142  		c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
   143  		return
   144  	}
   145  
   146  	cmd, appErr := c.App.GetCommand(c.Params.CommandId)
   147  	if appErr != nil {
   148  		c.SetCommandNotFoundError()
   149  		return
   150  	}
   151  	auditRec.AddMeta("command", cmd)
   152  
   153  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   154  		c.LogAudit("fail - inappropriate permissions")
   155  		// here we return Not_found instead of a permissions error so we don't leak the existence of
   156  		// a command to someone without permissions for the team it belongs to.
   157  		c.SetCommandNotFoundError()
   158  		return
   159  	}
   160  
   161  	if appErr = c.App.MoveCommand(newTeam, cmd); appErr != nil {
   162  		c.Err = appErr
   163  		return
   164  	}
   165  
   166  	auditRec.Success()
   167  	c.LogAudit("success")
   168  
   169  	ReturnStatusOK(w)
   170  }
   171  
   172  func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
   173  	c.RequireCommandId()
   174  	if c.Err != nil {
   175  		return
   176  	}
   177  
   178  	auditRec := c.MakeAuditRecord("deleteCommand", audit.Fail)
   179  	defer c.LogAuditRec(auditRec)
   180  	c.LogAudit("attempt")
   181  
   182  	cmd, err := c.App.GetCommand(c.Params.CommandId)
   183  	if err != nil {
   184  		c.SetCommandNotFoundError()
   185  		return
   186  	}
   187  	auditRec.AddMeta("command", cmd)
   188  
   189  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   190  		c.LogAudit("fail - inappropriate permissions")
   191  		// here we return Not_found instead of a permissions error so we don't leak the existence of
   192  		// a command to someone without permissions for the team it belongs to.
   193  		c.SetCommandNotFoundError()
   194  		return
   195  	}
   196  
   197  	if c.AppContext.Session().UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
   198  		c.LogAudit("fail - inappropriate permissions")
   199  		c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
   200  		return
   201  	}
   202  
   203  	err = c.App.DeleteCommand(cmd.Id)
   204  	if err != nil {
   205  		c.Err = err
   206  		return
   207  	}
   208  
   209  	auditRec.Success()
   210  	c.LogAudit("success")
   211  
   212  	ReturnStatusOK(w)
   213  }
   214  
   215  func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
   216  	customOnly, _ := strconv.ParseBool(r.URL.Query().Get("custom_only"))
   217  
   218  	teamId := r.URL.Query().Get("team_id")
   219  	if teamId == "" {
   220  		c.SetInvalidParam("team_id")
   221  		return
   222  	}
   223  
   224  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PERMISSION_VIEW_TEAM) {
   225  		c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
   226  		return
   227  	}
   228  
   229  	var commands []*model.Command
   230  	var err *model.AppError
   231  	if customOnly {
   232  		if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   233  			c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
   234  			return
   235  		}
   236  		commands, err = c.App.ListTeamCommands(teamId)
   237  		if err != nil {
   238  			c.Err = err
   239  			return
   240  		}
   241  	} else {
   242  		//User with no permission should see only system commands
   243  		if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   244  			commands, err = c.App.ListAutocompleteCommands(teamId, c.AppContext.T)
   245  			if err != nil {
   246  				c.Err = err
   247  				return
   248  			}
   249  		} else {
   250  			commands, err = c.App.ListAllCommands(teamId, c.AppContext.T)
   251  			if err != nil {
   252  				c.Err = err
   253  				return
   254  			}
   255  		}
   256  	}
   257  
   258  	w.Write([]byte(model.CommandListToJson(commands)))
   259  }
   260  
   261  func getCommand(c *Context, w http.ResponseWriter, r *http.Request) {
   262  	c.RequireCommandId()
   263  	if c.Err != nil {
   264  		return
   265  	}
   266  
   267  	cmd, err := c.App.GetCommand(c.Params.CommandId)
   268  	if err != nil {
   269  		c.SetCommandNotFoundError()
   270  		return
   271  	}
   272  
   273  	// check for permissions to view this command; must have perms to view team and
   274  	// PERMISSION_MANAGE_SLASH_COMMANDS for the team the command belongs to.
   275  
   276  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_VIEW_TEAM) {
   277  		// here we return Not_found instead of a permissions error so we don't leak the existence of
   278  		// a command to someone without permissions for the team it belongs to.
   279  		c.SetCommandNotFoundError()
   280  		return
   281  	}
   282  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   283  		// again, return not_found to ensure id existence does not leak.
   284  		c.SetCommandNotFoundError()
   285  		return
   286  	}
   287  	w.Write([]byte(cmd.ToJson()))
   288  }
   289  
   290  func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
   291  	commandArgs := model.CommandArgsFromJson(r.Body)
   292  	if commandArgs == nil {
   293  		c.SetInvalidParam("command_args")
   294  		return
   295  	}
   296  
   297  	if len(commandArgs.Command) <= 1 || strings.Index(commandArgs.Command, "/") != 0 || !model.IsValidId(commandArgs.ChannelId) {
   298  		c.Err = model.NewAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "", http.StatusBadRequest)
   299  		return
   300  	}
   301  
   302  	auditRec := c.MakeAuditRecord("executeCommand", audit.Fail)
   303  	defer c.LogAuditRec(auditRec)
   304  	auditRec.AddMeta("commandargs", commandArgs)
   305  
   306  	// checks that user is a member of the specified channel, and that they have permission to use slash commands in it
   307  	if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), commandArgs.ChannelId, model.PERMISSION_USE_SLASH_COMMANDS) {
   308  		c.SetPermissionError(model.PERMISSION_USE_SLASH_COMMANDS)
   309  		return
   310  	}
   311  
   312  	channel, err := c.App.GetChannel(commandArgs.ChannelId)
   313  	if err != nil {
   314  		c.Err = err
   315  		return
   316  	}
   317  
   318  	if channel.Type != model.CHANNEL_DIRECT && channel.Type != model.CHANNEL_GROUP {
   319  		// if this isn't a DM or GM, the team id is implicitly taken from the channel so that slash commands created on
   320  		// some other team can't be run against this one
   321  		commandArgs.TeamId = channel.TeamId
   322  	} else {
   323  		// if the slash command was used in a DM or GM, ensure that the user is a member of the specified team, so that
   324  		// they can't just execute slash commands against arbitrary teams
   325  		if c.AppContext.Session().GetTeamByTeamId(commandArgs.TeamId) == nil {
   326  			if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_USE_SLASH_COMMANDS) {
   327  				c.SetPermissionError(model.PERMISSION_USE_SLASH_COMMANDS)
   328  				return
   329  			}
   330  		}
   331  	}
   332  
   333  	commandArgs.UserId = c.AppContext.Session().UserId
   334  	commandArgs.T = c.AppContext.T
   335  	commandArgs.SiteURL = c.GetSiteURLHeader()
   336  	commandArgs.Session = *c.AppContext.Session()
   337  
   338  	auditRec.AddMeta("commandargs", commandArgs) // overwrite in case teamid changed
   339  
   340  	response, err := c.App.ExecuteCommand(c.AppContext, commandArgs)
   341  	if err != nil {
   342  		c.Err = err
   343  		return
   344  	}
   345  
   346  	auditRec.Success()
   347  	w.Write([]byte(response.ToJson()))
   348  }
   349  
   350  func listAutocompleteCommands(c *Context, w http.ResponseWriter, r *http.Request) {
   351  	c.RequireTeamId()
   352  	if c.Err != nil {
   353  		return
   354  	}
   355  
   356  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PERMISSION_VIEW_TEAM) {
   357  		c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
   358  		return
   359  	}
   360  
   361  	commands, err := c.App.ListAutocompleteCommands(c.Params.TeamId, c.AppContext.T)
   362  	if err != nil {
   363  		c.Err = err
   364  		return
   365  	}
   366  
   367  	w.Write([]byte(model.CommandListToJson(commands)))
   368  }
   369  
   370  func listCommandAutocompleteSuggestions(c *Context, w http.ResponseWriter, r *http.Request) {
   371  	c.RequireTeamId()
   372  	if c.Err != nil {
   373  		return
   374  	}
   375  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PERMISSION_VIEW_TEAM) {
   376  		c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
   377  		return
   378  	}
   379  
   380  	roleId := model.SYSTEM_USER_ROLE_ID
   381  	if c.IsSystemAdmin() {
   382  		roleId = model.SYSTEM_ADMIN_ROLE_ID
   383  	}
   384  
   385  	query := r.URL.Query()
   386  	userInput := query.Get("user_input")
   387  	if userInput == "" {
   388  		c.SetInvalidParam("userInput")
   389  		return
   390  	}
   391  	userInput = strings.TrimPrefix(userInput, "/")
   392  
   393  	commands, err := c.App.ListAutocompleteCommands(c.Params.TeamId, c.AppContext.T)
   394  	if err != nil {
   395  		c.Err = err
   396  		return
   397  	}
   398  
   399  	commandArgs := &model.CommandArgs{
   400  		ChannelId: query.Get("channel_id"),
   401  		TeamId:    c.Params.TeamId,
   402  		RootId:    query.Get("root_id"),
   403  		ParentId:  query.Get("parent_id"),
   404  		UserId:    c.AppContext.Session().UserId,
   405  		T:         c.AppContext.T,
   406  		Session:   *c.AppContext.Session(),
   407  		SiteURL:   c.GetSiteURLHeader(),
   408  		Command:   userInput,
   409  	}
   410  
   411  	suggestions := c.App.GetSuggestions(c.AppContext, commandArgs, commands, roleId)
   412  
   413  	w.Write(model.AutocompleteSuggestionsToJSON(suggestions))
   414  }
   415  
   416  func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
   417  	c.RequireCommandId()
   418  	if c.Err != nil {
   419  		return
   420  	}
   421  
   422  	auditRec := c.MakeAuditRecord("regenCommandToken", audit.Fail)
   423  	defer c.LogAuditRec(auditRec)
   424  	c.LogAudit("attempt")
   425  
   426  	cmd, err := c.App.GetCommand(c.Params.CommandId)
   427  	if err != nil {
   428  		auditRec.AddMeta("command_id", c.Params.CommandId)
   429  		c.SetCommandNotFoundError()
   430  		return
   431  	}
   432  	auditRec.AddMeta("command", cmd)
   433  
   434  	if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
   435  		c.LogAudit("fail - inappropriate permissions")
   436  		// here we return Not_found instead of a permissions error so we don't leak the existence of
   437  		// a command to someone without permissions for the team it belongs to.
   438  		c.SetCommandNotFoundError()
   439  		return
   440  	}
   441  
   442  	if c.AppContext.Session().UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
   443  		c.LogAudit("fail - inappropriate permissions")
   444  		c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
   445  		return
   446  	}
   447  
   448  	rcmd, err := c.App.RegenCommandToken(cmd)
   449  	if err != nil {
   450  		c.Err = err
   451  		return
   452  	}
   453  
   454  	auditRec.Success()
   455  	c.LogAudit("success")
   456  
   457  	resp := make(map[string]string)
   458  	resp["token"] = rcmd.Token
   459  
   460  	w.Write([]byte(model.MapToJson(resp)))
   461  }