summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/command.go144
-rw-r--r--api/command_test.go75
-rw-r--r--api/webhook.go58
-rw-r--r--model/client.go29
-rw-r--r--model/command.go9
-rw-r--r--model/utils.go6
6 files changed, 303 insertions, 18 deletions
diff --git a/api/command.go b/api/command.go
index 1e67453fb..d74643e15 100644
--- a/api/command.go
+++ b/api/command.go
@@ -37,37 +37,59 @@ func GetCommandProvidersProvider(name string) CommandProvider {
return nil
}
+// cmds = map[string]string{
+// "logoutCommand": "/logout",
+// "joinCommand": "/join",
+// "loadTestCommand": "/loadtest",
+// "echoCommand": "/echo",
+// "shrugCommand": "/shrug",
+// "meCommand": "/me",
+// }
+
func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes")
sr := r.PathPrefix("/commands").Subrouter()
- sr.Handle("/execute", ApiUserRequired(execute)).Methods("POST")
- sr.Handle("/list", ApiUserRequired(listCommands)).Methods("POST")
+ sr.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST")
+ sr.Handle("/list", ApiUserRequired(listCommands)).Methods("GET")
- sr.Handle("/create", ApiUserRequired(create)).Methods("POST")
+ sr.Handle("/create", ApiUserRequired(createCommand)).Methods("POST")
sr.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET")
- // sr.Handle("/regen_token", ApiUserRequired(regenOutgoingHookToken)).Methods("POST")
- // sr.Handle("/delete", ApiUserRequired(deleteOutgoingHook)).Methods("POST")
+ sr.Handle("/regen_token", ApiUserRequired(regenCommandToken)).Methods("POST")
+ sr.Handle("/delete", ApiUserRequired(deleteCommand)).Methods("POST")
}
func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
commands := make([]*model.Command, 0, 32)
+ seen := make(map[string]bool)
for _, value := range commandProviders {
cpy := *value.GetCommand()
- cpy.Token = ""
- cpy.CreatorId = ""
- cpy.Method = ""
- cpy.URL = ""
- cpy.Username = ""
- cpy.IconURL = ""
- commands = append(commands, &cpy)
+ if cpy.AutoComplete && !seen[cpy.Id] {
+ cpy.Sanatize()
+ seen[cpy.Trigger] = true
+ commands = append(commands, &cpy)
+ }
+ }
+
+ if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ teamCmds := result.Data.([]*model.Command)
+ for _, cmd := range teamCmds {
+ if cmd.AutoComplete && !seen[cmd.Id] {
+ cmd.Sanatize()
+ seen[cmd.Trigger] = true
+ commands = append(commands, cmd)
+ }
+ }
}
w.Write([]byte(model.CommandListToJson(commands)))
}
-func execute(c *Context, w http.ResponseWriter, r *http.Request) {
+func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
command := strings.TrimSpace(props["command"])
channelId := strings.TrimSpace(props["channelId"])
@@ -108,7 +130,7 @@ func execute(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func create(c *Context, w http.ResponseWriter, r *http.Request) {
+func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
if !*utils.Cfg.ServiceSettings.EnableCommands {
c.Err = model.NewAppError("createCommand", "Commands have been disabled by the system admin.", "")
c.Err.StatusCode = http.StatusNotImplemented
@@ -169,6 +191,100 @@ func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewAppError("createCommand", "Commands have been disabled by the system admin.", "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
+
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
+ c.LogAudit("attempt")
+
+ props := model.MapFromJson(r.Body)
+
+ id := props["id"]
+ if len(id) == 0 {
+ c.SetInvalidParam("regenCommandToken", "id")
+ return
+ }
+
+ var cmd *model.Command
+ if result := <-Srv.Store.Command().Get(id); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ cmd = result.Data.(*model.Command)
+
+ if c.Session.TeamId != cmd.TeamId && c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin() {
+ c.LogAudit("fail - inappropriate permissions")
+ c.Err = model.NewAppError("regenToken", "Inappropriate permissions to regenerate command token", "user_id="+c.Session.UserId)
+ return
+ }
+ }
+
+ cmd.Token = model.NewId()
+
+ if result := <-Srv.Store.Command().Update(cmd); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ w.Write([]byte(result.Data.(*model.Command).ToJson()))
+ }
+}
+
+func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewAppError("createCommand", "Commands have been disabled by the system admin.", "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
+
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
+ c.LogAudit("attempt")
+
+ props := model.MapFromJson(r.Body)
+
+ id := props["id"]
+ if len(id) == 0 {
+ c.SetInvalidParam("deleteCommand", "id")
+ return
+ }
+
+ if result := <-Srv.Store.Command().Get(id); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ if c.Session.TeamId != result.Data.(*model.Command).TeamId && c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin() {
+ c.LogAudit("fail - inappropriate permissions")
+ c.Err = model.NewAppError("deleteCommand", "Inappropriate permissions to delete command", "user_id="+c.Session.UserId)
+ return
+ }
+ }
+
+ if err := (<-Srv.Store.Command().Delete(id, model.GetMillis())).Err; err != nil {
+ c.Err = err
+ return
+ }
+
+ c.LogAudit("success")
+ w.Write([]byte(model.MapToJson(props)))
+}
+
// func command(c *Context, w http.ResponseWriter, r *http.Request) {
// props := model.MapFromJson(r.Body)
diff --git a/api/command_test.go b/api/command_test.go
index 8e0c2580e..b5c0558b8 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -107,6 +107,10 @@ func TestListTeamCommands(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
@@ -117,7 +121,7 @@ func TestListTeamCommands(t *testing.T) {
} else {
cmds := result.Data.([]*model.Command)
- if len(hooks) != 1 {
+ if len(cmds) != 1 {
t.Fatal("incorrect number of cmd")
}
}
@@ -125,6 +129,75 @@ func TestListTeamCommands(t *testing.T) {
*utils.Cfg.ServiceSettings.EnableCommands = false
}
+func TestRegenToken(t *testing.T) {
+ Setup()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
+
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
+
+ cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
+
+ data := make(map[string]string)
+ data["id"] = cmd.Id
+
+ if result, err := Client.RegenCommandToken(data); err != nil {
+ t.Fatal(err)
+ } else {
+ if result.Data.(*model.Command).Token == cmd.Token {
+ t.Fatal("regen didn't work properly")
+ }
+ }
+
+ *utils.Cfg.ServiceSettings.EnableCommands = false
+}
+
+func TestDeleteCommand(t *testing.T) {
+ Setup()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
+
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
+
+ cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
+
+ data := make(map[string]string)
+ data["id"] = cmd.Id
+
+ if _, err := Client.DeleteCommand(data); err != nil {
+ t.Fatal(err)
+ }
+
+ cmds := Client.Must(Client.ListTeamCommands()).Data.([]*model.Command)
+ if len(cmds) != 0 {
+ t.Fatal("delete didn't work properly")
+ }
+
+ *utils.Cfg.ServiceSettings.EnableCommands = false
+}
+
// func TestSuggestRootCommands(t *testing.T) {
// Setup()
diff --git a/api/webhook.go b/api/webhook.go
index 34c308879..de3d567ec 100644
--- a/api/webhook.go
+++ b/api/webhook.go
@@ -32,6 +32,14 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
hook := model.IncomingWebhookFromJson(r.Body)
@@ -79,6 +87,14 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -116,6 +132,14 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
if result := <-Srv.Store.Webhook().GetIncomingByUser(c.Session.UserId); result.Err != nil {
c.Err = result.Err
return
@@ -132,6 +156,14 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
hook := model.OutgoingWebhookFromJson(r.Body)
@@ -188,6 +220,14 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
if result := <-Srv.Store.Webhook().GetOutgoingByCreator(c.Session.UserId); result.Err != nil {
c.Err = result.Err
return
@@ -204,6 +244,14 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -241,6 +289,14 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewAppError("createCommand", "Integrations have been limited to admins only.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -258,7 +314,7 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
} else {
hook = result.Data.(*model.OutgoingWebhook)
- if c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
+ if c.Session.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
c.LogAudit("fail - inappropriate permissions")
c.Err = model.NewAppError("regenOutgoingHookToken", "Inappropriate permissions to regenerate outcoming webhook token", "user_id="+c.Session.UserId)
return
diff --git a/model/client.go b/model/client.go
index 83d1d316c..3a645e175 100644
--- a/model/client.go
+++ b/model/client.go
@@ -381,7 +381,16 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
}
func (c *Client) ListCommands() (*Result, *AppError) {
- if r, err := c.DoApiPost("/commands/list", ""); err != nil {
+ if r, err := c.DoApiGet("/commands/list", "", ""); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) ListTeamCommands() (*Result, *AppError) {
+ if r, err := c.DoApiGet("/commands/list_team_commands", "", ""); err != nil {
return nil, err
} else {
return &Result{r.Header.Get(HEADER_REQUEST_ID),
@@ -398,6 +407,24 @@ func (c *Client) CreateCommand(cmd *Command) (*Result, *AppError) {
}
}
+func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/commands/regen_token", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) DeleteCommand(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/commands/delete", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/"+id+"/audits", "", etag); err != nil {
return nil, err
diff --git a/model/command.go b/model/command.go
index 253021896..c917a46ea 100644
--- a/model/command.go
+++ b/model/command.go
@@ -133,3 +133,12 @@ func (o *Command) PreSave() {
func (o *Command) PreUpdate() {
o.UpdateAt = GetMillis()
}
+
+func (o *Command) Sanatize() {
+ o.Token = ""
+ o.CreatorId = ""
+ o.Method = ""
+ o.URL = ""
+ o.Username = ""
+ o.IconURL = ""
+}
diff --git a/model/utils.go b/model/utils.go
index 617c95efd..301e36f59 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -54,7 +54,11 @@ func AppErrorFromJson(data io.Reader) *AppError {
if err == nil {
return &er
} else {
- return NewAppError("AppErrorFromJson", "could not decode", err.Error())
+ buf := new(bytes.Buffer)
+ buf.ReadFrom(data)
+ s := buf.String()
+
+ return NewAppError("AppErrorFromJson", "could not decode", err.Error()+" "+s)
}
}