MM-24847 MM-24484 MM-24850 MM-24849 - local mode for commands (#14571)
* add unix socket listener for mmctl local mode * First working PoC * Adds the channel list endpoint * Add team list endpoint * Add a LocalClient to the api test helper and start local mode * Add helper to test with both SystemAdmin and Local clients * Add some docs * Adds TestForAllClients test helper * Incorporating @ashishbhate's proposal for adding test names to the helpers * [MM-24146] Add unix socket listener for mmctl local mode (#14296) * add unix socket listener for mmctl local mode * add a constant for local-mode socket path * reflect review comments * Fix init errors after merge * Adds create channel tests * Always init local mode to allow for enabling-disabling it via config * Check the RemoteAddr of the request before marking session as local * Mark the request as errored if it's local and the origin is remote * Set the socket permissions to read/write when initialising * Fix linter * Replace RemoteAddr check to ditch connections with the IP:PORT shape * added update/move/get/delete command in local mode * merge fix * . Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Miguel de la Cruz <miguel@mcrx.me>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
d1788cab85
Коммит
2af00f73c0
@@ -283,6 +283,7 @@ func InitLocal(configservice configservice.ConfigService, globalOptionsFunc app.
|
|||||||
api.BaseRoutes.Plugin = api.BaseRoutes.Plugins.PathPrefix("/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
|
api.BaseRoutes.Plugin = api.BaseRoutes.Plugins.PathPrefix("/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
|
||||||
|
|
||||||
api.BaseRoutes.Commands = api.BaseRoutes.ApiRoot.PathPrefix("/commands").Subrouter()
|
api.BaseRoutes.Commands = api.BaseRoutes.ApiRoot.PathPrefix("/commands").Subrouter()
|
||||||
|
api.BaseRoutes.Command = api.BaseRoutes.Commands.PathPrefix("/{command_id:[A-Za-z0-9]+}").Subrouter()
|
||||||
|
|
||||||
api.BaseRoutes.License = api.BaseRoutes.ApiRoot.PathPrefix("/license").Subrouter()
|
api.BaseRoutes.License = api.BaseRoutes.ApiRoot.PathPrefix("/license").Subrouter()
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import (
|
|||||||
func (api *API) InitCommandLocal() {
|
func (api *API) InitCommandLocal() {
|
||||||
api.BaseRoutes.Commands.Handle("", api.ApiLocal(localCreateCommand)).Methods("POST")
|
api.BaseRoutes.Commands.Handle("", api.ApiLocal(localCreateCommand)).Methods("POST")
|
||||||
api.BaseRoutes.Commands.Handle("", api.ApiLocal(listCommands)).Methods("GET")
|
api.BaseRoutes.Commands.Handle("", api.ApiLocal(listCommands)).Methods("GET")
|
||||||
|
|
||||||
|
api.BaseRoutes.Command.Handle("", api.ApiLocal(getCommand)).Methods("GET")
|
||||||
|
api.BaseRoutes.Command.Handle("", api.ApiLocal(updateCommand)).Methods("PUT")
|
||||||
|
api.BaseRoutes.Command.Handle("/move", api.ApiLocal(moveCommand)).Methods("PUT")
|
||||||
|
api.BaseRoutes.Command.Handle("", api.ApiLocal(deleteCommand)).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
|
||||||
func localCreateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
func localCreateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ func TestCreateCommand(t *testing.T) {
|
|||||||
func TestUpdateCommand(t *testing.T) {
|
func TestUpdateCommand(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.SystemAdminClient
|
|
||||||
user := th.SystemAdminUser
|
user := th.SystemAdminUser
|
||||||
team := th.BasicTeam
|
team := th.BasicTeam
|
||||||
|
|
||||||
@@ -107,51 +106,51 @@ func TestUpdateCommand(t *testing.T) {
|
|||||||
Token: "tokenchange",
|
Token: "tokenchange",
|
||||||
}
|
}
|
||||||
|
|
||||||
rcmd, resp := Client.UpdateCommand(cmd2)
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
||||||
CheckNoError(t, resp)
|
rcmd, resp := client.UpdateCommand(cmd2)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
require.Equal(t, cmd2.Trigger, rcmd.Trigger, "Trigger should have updated")
|
require.Equal(t, cmd2.Trigger, rcmd.Trigger, "Trigger should have updated")
|
||||||
|
|
||||||
require.Equal(t, cmd2.Method, rcmd.Method, "Method should have updated")
|
require.Equal(t, cmd2.Method, rcmd.Method, "Method should have updated")
|
||||||
|
|
||||||
require.Equal(t, cmd2.URL, rcmd.URL, "URL should have updated")
|
require.Equal(t, cmd2.URL, rcmd.URL, "URL should have updated")
|
||||||
|
|
||||||
require.Equal(t, cmd1.CreatorId, rcmd.CreatorId, "CreatorId should have not updated")
|
require.Equal(t, cmd1.CreatorId, rcmd.CreatorId, "CreatorId should have not updated")
|
||||||
|
|
||||||
require.Equal(t, cmd1.Token, rcmd.Token, "Token should have not updated")
|
require.Equal(t, cmd1.Token, rcmd.Token, "Token should have not updated")
|
||||||
|
|
||||||
cmd2.Id = GenerateTestId()
|
cmd2.Id = GenerateTestId()
|
||||||
|
|
||||||
rcmd, resp = Client.UpdateCommand(cmd2)
|
rcmd, resp = client.UpdateCommand(cmd2)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
|
|
||||||
require.Nil(t, rcmd, "should be empty")
|
require.Nil(t, rcmd, "should be empty")
|
||||||
|
|
||||||
cmd2.Id = "junk"
|
cmd2.Id = "junk"
|
||||||
|
|
||||||
_, resp = Client.UpdateCommand(cmd2)
|
_, resp = client.UpdateCommand(cmd2)
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
cmd2.Id = cmd1.Id
|
cmd2.Id = cmd1.Id
|
||||||
cmd2.TeamId = GenerateTestId()
|
cmd2.TeamId = GenerateTestId()
|
||||||
|
|
||||||
_, resp = Client.UpdateCommand(cmd2)
|
_, resp = client.UpdateCommand(cmd2)
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
cmd2.TeamId = team.Id
|
cmd2.TeamId = team.Id
|
||||||
|
|
||||||
_, resp = th.Client.UpdateCommand(cmd2)
|
_, resp = th.Client.UpdateCommand(cmd2)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
|
})
|
||||||
Client.Logout()
|
th.SystemAdminClient.Logout()
|
||||||
_, resp = Client.UpdateCommand(cmd2)
|
_, resp := th.SystemAdminClient.UpdateCommand(cmd2)
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMoveCommand(t *testing.T) {
|
func TestMoveCommand(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.SystemAdminClient
|
|
||||||
user := th.SystemAdminUser
|
user := th.SystemAdminUser
|
||||||
team := th.BasicTeam
|
team := th.BasicTeam
|
||||||
newTeam := th.CreateTeam()
|
newTeam := th.CreateTeam()
|
||||||
@@ -171,23 +170,24 @@ func TestMoveCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rcmd1, _ := th.App.CreateCommand(cmd1)
|
rcmd1, _ := th.App.CreateCommand(cmd1)
|
||||||
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
||||||
|
|
||||||
ok, resp := Client.MoveCommand(newTeam.Id, rcmd1.Id)
|
ok, resp := client.MoveCommand(newTeam.Id, rcmd1.Id)
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
rcmd1, _ = th.App.GetCommand(rcmd1.Id)
|
rcmd1, _ = th.App.GetCommand(rcmd1.Id)
|
||||||
require.NotNil(t, rcmd1)
|
require.NotNil(t, rcmd1)
|
||||||
require.Equal(t, newTeam.Id, rcmd1.TeamId)
|
require.Equal(t, newTeam.Id, rcmd1.TeamId)
|
||||||
|
|
||||||
ok, resp = Client.MoveCommand(newTeam.Id, "bogus")
|
ok, resp = client.MoveCommand(newTeam.Id, "bogus")
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
|
|
||||||
ok, resp = Client.MoveCommand(GenerateTestId(), rcmd1.Id)
|
|
||||||
CheckNotFoundStatus(t, resp)
|
|
||||||
require.False(t, ok)
|
|
||||||
|
|
||||||
|
ok, resp = client.MoveCommand(GenerateTestId(), rcmd1.Id)
|
||||||
|
CheckNotFoundStatus(t, resp)
|
||||||
|
require.False(t, ok)
|
||||||
|
})
|
||||||
cmd2 := &model.Command{
|
cmd2 := &model.Command{
|
||||||
CreatorId: user.Id,
|
CreatorId: user.Id,
|
||||||
TeamId: team.Id,
|
TeamId: team.Id,
|
||||||
@@ -198,18 +198,17 @@ func TestMoveCommand(t *testing.T) {
|
|||||||
|
|
||||||
rcmd2, _ := th.App.CreateCommand(cmd2)
|
rcmd2, _ := th.App.CreateCommand(cmd2)
|
||||||
|
|
||||||
_, resp = th.Client.MoveCommand(newTeam.Id, rcmd2.Id)
|
_, resp := th.Client.MoveCommand(newTeam.Id, rcmd2.Id)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
|
|
||||||
Client.Logout()
|
th.SystemAdminClient.Logout()
|
||||||
_, resp = Client.MoveCommand(newTeam.Id, rcmd2.Id)
|
_, resp = th.SystemAdminClient.MoveCommand(newTeam.Id, rcmd2.Id)
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteCommand(t *testing.T) {
|
func TestDeleteCommand(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.SystemAdminClient
|
|
||||||
user := th.SystemAdminUser
|
user := th.SystemAdminUser
|
||||||
team := th.BasicTeam
|
team := th.BasicTeam
|
||||||
|
|
||||||
@@ -227,24 +226,26 @@ func TestDeleteCommand(t *testing.T) {
|
|||||||
Trigger: "trigger1",
|
Trigger: "trigger1",
|
||||||
}
|
}
|
||||||
|
|
||||||
rcmd1, _ := th.App.CreateCommand(cmd1)
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
||||||
|
cmd1.Id = ""
|
||||||
|
rcmd1, err := th.App.CreateCommand(cmd1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
ok, resp := client.DeleteCommand(rcmd1.Id)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
ok, resp := Client.DeleteCommand(rcmd1.Id)
|
require.True(t, ok)
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
require.True(t, ok)
|
rcmd1, _ = th.App.GetCommand(rcmd1.Id)
|
||||||
|
require.Nil(t, rcmd1)
|
||||||
|
|
||||||
rcmd1, _ = th.App.GetCommand(rcmd1.Id)
|
ok, resp = client.DeleteCommand("junk")
|
||||||
require.Nil(t, rcmd1)
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
ok, resp = Client.DeleteCommand("junk")
|
require.False(t, ok)
|
||||||
CheckBadRequestStatus(t, resp)
|
|
||||||
|
|
||||||
require.False(t, ok)
|
|
||||||
|
|
||||||
_, resp = Client.DeleteCommand(GenerateTestId())
|
|
||||||
CheckNotFoundStatus(t, resp)
|
|
||||||
|
|
||||||
|
_, resp = client.DeleteCommand(GenerateTestId())
|
||||||
|
CheckNotFoundStatus(t, resp)
|
||||||
|
})
|
||||||
cmd2 := &model.Command{
|
cmd2 := &model.Command{
|
||||||
CreatorId: user.Id,
|
CreatorId: user.Id,
|
||||||
TeamId: team.Id,
|
TeamId: team.Id,
|
||||||
@@ -255,11 +256,11 @@ func TestDeleteCommand(t *testing.T) {
|
|||||||
|
|
||||||
rcmd2, _ := th.App.CreateCommand(cmd2)
|
rcmd2, _ := th.App.CreateCommand(cmd2)
|
||||||
|
|
||||||
_, resp = th.Client.DeleteCommand(rcmd2.Id)
|
_, resp := th.Client.DeleteCommand(rcmd2.Id)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
|
|
||||||
Client.Logout()
|
th.SystemAdminClient.Logout()
|
||||||
_, resp = Client.DeleteCommand(rcmd2.Id)
|
_, resp = th.SystemAdminClient.DeleteCommand(rcmd2.Id)
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +514,6 @@ func TestListCommandAutocompleteSuggestions(t *testing.T) {
|
|||||||
func TestGetCommand(t *testing.T) {
|
func TestGetCommand(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -530,41 +530,42 @@ func TestGetCommand(t *testing.T) {
|
|||||||
|
|
||||||
newCmd, resp := th.SystemAdminClient.CreateCommand(newCmd)
|
newCmd, resp := th.SystemAdminClient.CreateCommand(newCmd)
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
||||||
|
|
||||||
t.Run("ValidId", func(t *testing.T) {
|
t.Run("ValidId", func(t *testing.T) {
|
||||||
cmd, resp := th.SystemAdminClient.GetCommandById(newCmd.Id)
|
cmd, resp := client.GetCommandById(newCmd.Id)
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
require.Equal(t, newCmd.Id, cmd.Id)
|
require.Equal(t, newCmd.Id, cmd.Id)
|
||||||
require.Equal(t, newCmd.CreatorId, cmd.CreatorId)
|
require.Equal(t, newCmd.CreatorId, cmd.CreatorId)
|
||||||
require.Equal(t, newCmd.TeamId, cmd.TeamId)
|
require.Equal(t, newCmd.TeamId, cmd.TeamId)
|
||||||
require.Equal(t, newCmd.URL, cmd.URL)
|
require.Equal(t, newCmd.URL, cmd.URL)
|
||||||
require.Equal(t, newCmd.Method, cmd.Method)
|
require.Equal(t, newCmd.Method, cmd.Method)
|
||||||
require.Equal(t, newCmd.Trigger, cmd.Trigger)
|
require.Equal(t, newCmd.Trigger, cmd.Trigger)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("InvalidId", func(t *testing.T) {
|
||||||
|
_, resp := client.GetCommandById(strings.Repeat("z", len(newCmd.Id)))
|
||||||
|
require.Error(t, resp.Error)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("InvalidId", func(t *testing.T) {
|
|
||||||
_, resp := th.SystemAdminClient.GetCommandById(strings.Repeat("z", len(newCmd.Id)))
|
|
||||||
require.Error(t, resp.Error)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("UserWithNoPermissionForCustomCommands", func(t *testing.T) {
|
t.Run("UserWithNoPermissionForCustomCommands", func(t *testing.T) {
|
||||||
_, resp := Client.GetCommandById(newCmd.Id)
|
_, resp := th.Client.GetCommandById(newCmd.Id)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NoMember", func(t *testing.T) {
|
t.Run("NoMember", func(t *testing.T) {
|
||||||
Client.Logout()
|
th.Client.Logout()
|
||||||
user := th.CreateUser()
|
user := th.CreateUser()
|
||||||
th.SystemAdminClient.RemoveTeamMember(th.BasicTeam.Id, user.Id)
|
th.SystemAdminClient.RemoveTeamMember(th.BasicTeam.Id, user.Id)
|
||||||
Client.Login(user.Email, user.Password)
|
th.Client.Login(user.Email, user.Password)
|
||||||
_, resp := Client.GetCommandById(newCmd.Id)
|
_, resp := th.Client.GetCommandById(newCmd.Id)
|
||||||
CheckNotFoundStatus(t, resp)
|
CheckNotFoundStatus(t, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NotLoggedIn", func(t *testing.T) {
|
t.Run("NotLoggedIn", func(t *testing.T) {
|
||||||
Client.Logout()
|
th.Client.Logout()
|
||||||
_, resp := Client.GetCommandById(newCmd.Id)
|
_, resp := th.Client.GetCommandById(newCmd.Id)
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func (a *App) SessionHasPermissionToTeam(session model.Session, teamId string, p
|
|||||||
if session.IsUnrestricted() {
|
if session.IsUnrestricted() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
teamMember := session.GetTeamByTeamId(teamId)
|
teamMember := session.GetTeamByTeamId(teamId)
|
||||||
if teamMember != nil {
|
if teamMember != nil {
|
||||||
if a.RolesGrantPermission(teamMember.GetRoles(), permission.Id) {
|
if a.RolesGrantPermission(teamMember.GetRoles(), permission.Id) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user