From 3187907b673ee2abdd32954978fbd3689e125e0c Mon Sep 17 00:00:00 2001 From: Maria A Nunez Date: Mon, 29 Jul 2019 12:32:26 -0400 Subject: [PATCH] MM-16990 - Fix webhooks visible to users without viewing permissions (#11698) * Filtered incoming webhooks for users wihtout PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS * Filtered outgoing webhooks for users without PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS * Refactored GetOutgoingByTeamByUser to its own method in app and store * Fixed paging condition for outgoing webhooks in store * Separated test cases into separate t.run in WebhookStore * Improved unit test. PR Feedback * Filtered outgoing webhooks by channel for users without PERMISSION_MANAGE_OTHERS * Filtered getting full list of outgoing webhooks for users without PERMISSION_MANAGE_OTHERS * Added missing signature for GetOutgoingWebhooksPage in app * Expanded permissions in test to SYSTEM_USER_ROLE * Filtered getting full list of incoming webhooks for users without PERMISSION_MANAGE_OTHERS * Removed unnecessary sq.and operator --- api4/webhook.go | 37 ++++- api4/webhook_test.go | 212 +++++++++++++++++++++++++- app/webhook.go | 30 +++- store/sqlstore/webhook_store.go | 133 +++++++++++++--- store/store.go | 5 + store/storetest/mocks/WebhookStore.go | 125 +++++++++++++++ store/storetest/webhook_store.go | 164 ++++++++++++++++++++ 7 files changed, 671 insertions(+), 35 deletions(-) diff --git a/api4/webhook.go b/api4/webhook.go index 8dc1e9cb65..18077d3a62 100644 --- a/api4/webhook.go +++ b/api4/webhook.go @@ -137,6 +137,7 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { teamId := r.URL.Query().Get("team_id") + userId := c.App.Session.UserId var hooks []*model.IncomingWebhook var err *model.AppError @@ -147,14 +148,24 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { return } - hooks, err = c.App.GetIncomingWebhooksForTeamPage(teamId, c.Params.Page, c.Params.PerPage) + // Remove userId as a filter if they have permission to manage others. + if c.App.SessionHasPermissionToTeam(c.App.Session, teamId, model.PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS) { + userId = "" + } + + hooks, err = c.App.GetIncomingWebhooksForTeamPageByUser(teamId, userId, c.Params.Page, c.Params.PerPage) } else { if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_INCOMING_WEBHOOKS) { c.SetPermissionError(model.PERMISSION_MANAGE_INCOMING_WEBHOOKS) return } - hooks, err = c.App.GetIncomingWebhooksPage(c.Params.Page, c.Params.PerPage) + // Remove userId as a filter if they have permission to manage others. + if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS) { + userId = "" + } + + hooks, err = c.App.GetIncomingWebhooksPageByUser(userId, c.Params.Page, c.Params.PerPage) } if err != nil { @@ -339,6 +350,7 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { channelId := r.URL.Query().Get("channel_id") teamId := r.URL.Query().Get("team_id") + userId := c.App.Session.UserId var hooks []*model.OutgoingWebhook var err *model.AppError @@ -349,21 +361,36 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { return } - hooks, err = c.App.GetOutgoingWebhooksForChannelPage(channelId, c.Params.Page, c.Params.PerPage) + // Remove userId as a filter if they have permission to manage others. + if c.App.SessionHasPermissionToChannel(c.App.Session, channelId, model.PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS) { + userId = "" + } + + hooks, err = c.App.GetOutgoingWebhooksForChannelPageByUser(channelId, userId, c.Params.Page, c.Params.PerPage) } else if len(teamId) > 0 { if !c.App.SessionHasPermissionToTeam(c.App.Session, teamId, model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) { c.SetPermissionError(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) return } - hooks, err = c.App.GetOutgoingWebhooksForTeamPage(teamId, c.Params.Page, c.Params.PerPage) + // Remove userId as a filter if they have permission to manage others. + if c.App.SessionHasPermissionToTeam(c.App.Session, teamId, model.PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS) { + userId = "" + } + + hooks, err = c.App.GetOutgoingWebhooksForTeamPageByUser(teamId, userId, c.Params.Page, c.Params.PerPage) } else { if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) { c.SetPermissionError(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) return } - hooks, err = c.App.GetOutgoingWebhooksPage(c.Params.Page, c.Params.PerPage) + // Remove userId as a filter if they have permission to manage others. + if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS) { + userId = "" + } + + hooks, err = c.App.GetOutgoingWebhooksPageByUser(userId, c.Params.Page, c.Params.PerPage) } if err != nil { diff --git a/api4/webhook_test.go b/api4/webhook_test.go index 025b2475c7..ac7b85fae5 100644 --- a/api4/webhook_test.go +++ b/api4/webhook_test.go @@ -74,7 +74,6 @@ func TestCreateIncomingWebhook(t *testing.T) { CheckNotImplementedStatus(t, resp) } - func TestCreateIncomingWebhook_BypassTeamPermissions(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() @@ -96,7 +95,7 @@ func TestCreateIncomingWebhook_BypassTeamPermissions(t *testing.T) { require.Equal(t, rhook.ChannelId, hook.ChannelId) require.Equal(t, rhook.UserId, th.BasicUser.Id) - require.Equal(t, rhook.TeamId,th.BasicTeam.Id) + require.Equal(t, rhook.TeamId, th.BasicTeam.Id) team := th.CreateTeam() team.AllowOpenInvite = false @@ -188,6 +187,88 @@ func TestGetIncomingWebhooks(t *testing.T) { CheckUnauthorizedStatus(t, resp) } +func TestGetIncomingWebhooksListByUser(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + BasicClient := th.Client + th.LoginBasic() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id, model.SYSTEM_USER_ROLE_ID) + + // Basic user webhook + bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id} + basicHook, resp := BasicClient.CreateIncomingWebhook(bHook) + CheckNoError(t, resp) + + basicHooks, resp := BasicClient.GetIncomingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(basicHooks)) + assert.Equal(t, basicHook.Id, basicHooks[0].Id) + + // Admin User webhook + aHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.SystemAdminUser.Id} + _, resp = th.SystemAdminClient.CreateIncomingWebhook(aHook) + CheckNoError(t, resp) + + adminHooks, resp := th.SystemAdminClient.GetIncomingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 2, len(adminHooks)) + + //Re-check basic user that has no MANAGE_OTHERS permission + filteredHooks, resp := BasicClient.GetIncomingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(filteredHooks)) + assert.Equal(t, basicHook.Id, filteredHooks[0].Id) +} + +func TestGetIncomingWebhooksByTeam(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + BasicClient := th.Client + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + + // Basic user webhook + bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id} + basicHook, resp := BasicClient.CreateIncomingWebhook(bHook) + CheckNoError(t, resp) + + basicHooks, resp := BasicClient.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(basicHooks)) + assert.Equal(t, basicHook.Id, basicHooks[0].Id) + + // Admin User webhook + aHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.SystemAdminUser.Id} + _, resp = th.SystemAdminClient.CreateIncomingWebhook(aHook) + CheckNoError(t, resp) + + adminHooks, resp := th.SystemAdminClient.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 2, len(adminHooks)) + + //Re-check basic user that has no MANAGE_OTHERS permission + filteredHooks, resp := BasicClient.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(filteredHooks)) + assert.Equal(t, basicHook.Id, filteredHooks[0].Id) + +} + func TestGetIncomingWebhook(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() @@ -429,6 +510,129 @@ func TestGetOutgoingWebhooks(t *testing.T) { CheckUnauthorizedStatus(t, resp) } +func TestGetOutgoingWebhooksByTeam(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + BasicClient := th.Client + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + + // Basic user webhook + bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + basicHook, resp := BasicClient.CreateOutgoingWebhook(bHook) + CheckNoError(t, resp) + + basicHooks, resp := BasicClient.GetOutgoingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(basicHooks)) + assert.Equal(t, basicHook.Id, basicHooks[0].Id) + + // Admin User webhook + aHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + _, resp = th.SystemAdminClient.CreateOutgoingWebhook(aHook) + CheckNoError(t, resp) + + adminHooks, resp := th.SystemAdminClient.GetOutgoingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 2, len(adminHooks)) + + //Re-check basic user that has no MANAGE_OTHERS permission + filteredHooks, resp := BasicClient.GetOutgoingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(filteredHooks)) + assert.Equal(t, basicHook.Id, filteredHooks[0].Id) + +} + +func TestGetOutgoingWebhooksByChannel(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + BasicClient := th.Client + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + + // Basic user webhook + bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + basicHook, resp := BasicClient.CreateOutgoingWebhook(bHook) + CheckNoError(t, resp) + + basicHooks, resp := BasicClient.GetOutgoingWebhooksForChannel(th.BasicChannel.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(basicHooks)) + assert.Equal(t, basicHook.Id, basicHooks[0].Id) + + // Admin User webhook + aHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + _, resp = th.SystemAdminClient.CreateOutgoingWebhook(aHook) + CheckNoError(t, resp) + + adminHooks, resp := th.SystemAdminClient.GetOutgoingWebhooksForChannel(th.BasicChannel.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 2, len(adminHooks)) + + //Re-check basic user that has no MANAGE_OTHERS permission + filteredHooks, resp := BasicClient.GetOutgoingWebhooksForChannel(th.BasicChannel.Id, 0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(filteredHooks)) + assert.Equal(t, basicHook.Id, filteredHooks[0].Id) + +} + +func TestGetOutgoingWebhooksListByUser(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + BasicClient := th.Client + th.LoginBasic() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.SYSTEM_USER_ROLE_ID) + + // Basic user webhook + bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + basicHook, resp := BasicClient.CreateOutgoingWebhook(bHook) + CheckNoError(t, resp) + + basicHooks, resp := BasicClient.GetOutgoingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(basicHooks)) + assert.Equal(t, basicHook.Id, basicHooks[0].Id) + + // Admin User webhook + aHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} + _, resp = th.SystemAdminClient.CreateOutgoingWebhook(aHook) + CheckNoError(t, resp) + + adminHooks, resp := th.SystemAdminClient.GetOutgoingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 2, len(adminHooks)) + + //Re-check basic user that has no MANAGE_OTHERS permission + filteredHooks, resp := BasicClient.GetOutgoingWebhooks(0, 1000, "") + CheckNoError(t, resp) + assert.Equal(t, 1, len(filteredHooks)) + assert.Equal(t, basicHook.Id, filteredHooks[0].Id) + +} func TestGetOutgoingWebhook(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() @@ -695,7 +899,7 @@ func TestUpdateIncomingWebhook_BypassTeamPermissions(t *testing.T) { require.Equal(t, rhook.ChannelId, hook.ChannelId) require.Equal(t, rhook.UserId, th.BasicUser.Id) - require.Equal(t, rhook.TeamId,th.BasicTeam.Id) + require.Equal(t, rhook.TeamId, th.BasicTeam.Id) team := th.CreateTeam() team.AllowOpenInvite = false @@ -922,7 +1126,7 @@ func TestUpdateOutgoingWebhook_BypassTeamPermissions(t *testing.T) { CheckNoError(t, resp) require.Equal(t, rhook.ChannelId, hook.ChannelId) - require.Equal(t, rhook.TeamId,th.BasicTeam.Id) + require.Equal(t, rhook.TeamId, th.BasicTeam.Id) team := th.CreateTeam() team.AllowOpenInvite = false diff --git a/app/webhook.go b/app/webhook.go index daa4856cd9..ae7491ce5a 100644 --- a/app/webhook.go +++ b/app/webhook.go @@ -381,19 +381,27 @@ func (a *App) GetIncomingWebhook(hookId string) (*model.IncomingWebhook, *model. } func (a *App) GetIncomingWebhooksForTeamPage(teamId string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { + return a.GetIncomingWebhooksForTeamPageByUser(teamId, "", page, perPage) +} + +func (a *App) GetIncomingWebhooksForTeamPageByUser(teamId string, userId string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { if !*a.Config().ServiceSettings.EnableIncomingWebhooks { return nil, model.NewAppError("GetIncomingWebhooksForTeamPage", "api.incoming_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - return a.Srv.Store.Webhook().GetIncomingByTeam(teamId, page*perPage, perPage) + return a.Srv.Store.Webhook().GetIncomingByTeamByUser(teamId, userId, page*perPage, perPage) } -func (a *App) GetIncomingWebhooksPage(page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { +func (a *App) GetIncomingWebhooksPageByUser(userId string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { if !*a.Config().ServiceSettings.EnableIncomingWebhooks { return nil, model.NewAppError("GetIncomingWebhooksPage", "api.incoming_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - return a.Srv.Store.Webhook().GetIncomingList(page*perPage, perPage) + return a.Srv.Store.Webhook().GetIncomingListByUser(userId, page*perPage, perPage) +} + +func (a *App) GetIncomingWebhooksPage(page, perPage int) ([]*model.IncomingWebhook, *model.AppError) { + return a.GetIncomingWebhooksPageByUser("", page, perPage) } func (a *App) CreateOutgoingWebhook(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError) { @@ -494,27 +502,35 @@ func (a *App) GetOutgoingWebhook(hookId string) (*model.OutgoingWebhook, *model. } func (a *App) GetOutgoingWebhooksPage(page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { + return a.GetOutgoingWebhooksPageByUser("", page, perPage) +} + +func (a *App) GetOutgoingWebhooksPageByUser(userId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { if !*a.Config().ServiceSettings.EnableOutgoingWebhooks { return nil, model.NewAppError("GetOutgoingWebhooksPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - return a.Srv.Store.Webhook().GetOutgoingList(page*perPage, perPage) + return a.Srv.Store.Webhook().GetOutgoingListByUser(userId, page*perPage, perPage) } -func (a *App) GetOutgoingWebhooksForChannelPage(channelId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { +func (a *App) GetOutgoingWebhooksForChannelPageByUser(channelId string, userId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { if !*a.Config().ServiceSettings.EnableOutgoingWebhooks { return nil, model.NewAppError("GetOutgoingWebhooksForChannelPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - return a.Srv.Store.Webhook().GetOutgoingByChannel(channelId, page*perPage, perPage) + return a.Srv.Store.Webhook().GetOutgoingByChannelByUser(channelId, userId, page*perPage, perPage) } func (a *App) GetOutgoingWebhooksForTeamPage(teamId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { + return a.GetOutgoingWebhooksForTeamPageByUser(teamId, "", page, perPage) +} + +func (a *App) GetOutgoingWebhooksForTeamPageByUser(teamId string, userId string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError) { if !*a.Config().ServiceSettings.EnableOutgoingWebhooks { return nil, model.NewAppError("GetOutgoingWebhooksForTeamPage", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented) } - return a.Srv.Store.Webhook().GetOutgoingByTeam(teamId, page*perPage, perPage) + return a.Srv.Store.Webhook().GetOutgoingByTeamByUser(teamId, userId, page*perPage, perPage) } func (a *App) DeleteOutgoingWebhook(hookId string) *model.AppError { diff --git a/store/sqlstore/webhook_store.go b/store/sqlstore/webhook_store.go index 200dfb7384..51e67962eb 100644 --- a/store/sqlstore/webhook_store.go +++ b/store/sqlstore/webhook_store.go @@ -7,6 +7,7 @@ import ( "database/sql" "net/http" + sq "github.com/Masterminds/squirrel" "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" @@ -174,9 +175,27 @@ func (s SqlWebhookStore) PermanentDeleteIncomingByChannel(channelId string) *mod } func (s SqlWebhookStore) GetIncomingList(offset, limit int) ([]*model.IncomingWebhook, *model.AppError) { + return s.GetIncomingListByUser("", offset, limit) +} + +func (s SqlWebhookStore) GetIncomingListByUser(userId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) { var webhooks []*model.IncomingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Limit": limit, "Offset": offset}); err != nil { + query := s.getQueryBuilder(). + Select("*"). + From("IncomingWebhooks"). + Where(sq.Eq{"DeleteAt": int(0)}).Limit(uint64(limit)).Offset(uint64(offset)) + + if len(userId) > 0 { + query = query.Where(sq.Eq{"UserId": userId}) + } + + queryString, args, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlWebhookStore.GetIncomingList", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) + } + + if _, err := s.GetReplica().Select(&webhooks, queryString, args...); err != nil { return nil, model.NewAppError("SqlWebhookStore.GetIncomingList", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) } @@ -184,16 +203,37 @@ func (s SqlWebhookStore) GetIncomingList(offset, limit int) ([]*model.IncomingWe } -func (s SqlWebhookStore) GetIncomingByTeam(teamId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) { +func (s SqlWebhookStore) GetIncomingByTeamByUser(teamId string, userId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) { var webhooks []*model.IncomingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset}); err != nil { + query := s.getQueryBuilder(). + Select("*"). + From("IncomingWebhooks"). + Where(sq.And{ + sq.Eq{"TeamId": teamId}, + sq.Eq{"DeleteAt": int(0)}, + }).Limit(uint64(limit)).Offset(uint64(offset)) + + if len(userId) > 0 { + query = query.Where(sq.Eq{"UserId": userId}) + } + + queryString, args, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) + } + + if _, err := s.GetReplica().Select(&webhooks, queryString, args...); err != nil { return nil, model.NewAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) } return webhooks, nil } +func (s SqlWebhookStore) GetIncomingByTeam(teamId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) { + return s.GetIncomingByTeamByUser(teamId, "", offset, limit) +} + func (s SqlWebhookStore) GetIncomingByChannel(channelId string) ([]*model.IncomingWebhook, *model.AppError) { var webhooks []*model.IncomingWebhook @@ -232,50 +272,105 @@ func (s SqlWebhookStore) GetOutgoing(id string) (*model.OutgoingWebhook, *model. return &webhook, nil } -func (s SqlWebhookStore) GetOutgoingList(offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { +func (s SqlWebhookStore) GetOutgoingListByUser(userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { var webhooks []*model.OutgoingWebhook - if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil { + query := s.getQueryBuilder(). + Select("*"). + From("OutgoingWebhooks"). + Where(sq.And{ + sq.Eq{"DeleteAt": int(0)}, + }).Limit(uint64(limit)).Offset(uint64(offset)) + + if len(userId) > 0 { + query = query.Where(sq.Eq{"CreatorId": userId}) + } + + queryString, args, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlWebhookStore.GetOutgoingByChannel", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + if _, err := s.GetReplica().Select(&webhooks, queryString, args...); err != nil { return nil, model.NewAppError("SqlWebhookStore.GetOutgoingList", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) } return webhooks, nil } -func (s SqlWebhookStore) GetOutgoingByChannel(channelId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { +func (s SqlWebhookStore) GetOutgoingList(offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + return s.GetOutgoingListByUser("", offset, limit) + +} + +func (s SqlWebhookStore) GetOutgoingByChannelByUser(channelId string, userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { var webhooks []*model.OutgoingWebhook - query := "" - if limit < 0 || offset < 0 { - query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0" - } else { - query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset" + query := s.getQueryBuilder(). + Select("*"). + From("OutgoingWebhooks"). + Where(sq.And{ + sq.Eq{"ChannelId": channelId}, + sq.Eq{"DeleteAt": int(0)}, + }) + + if len(userId) > 0 { + query = query.Where(sq.Eq{"CreatorId": userId}) + } + if limit >= 0 && offset >= 0 { + query = query.Limit(uint64(limit)).Offset(uint64(offset)) } - if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil { + queryString, args, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlWebhookStore.GetOutgoingByChannel", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + if _, err := s.GetReplica().Select(&webhooks, queryString, args...); err != nil { return nil, model.NewAppError("SqlWebhookStore.GetOutgoingByChannel", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError) } return webhooks, nil } -func (s SqlWebhookStore) GetOutgoingByTeam(teamId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { +func (s SqlWebhookStore) GetOutgoingByChannel(channelId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + return s.GetOutgoingByChannelByUser(channelId, "", offset, limit) +} + +func (s SqlWebhookStore) GetOutgoingByTeamByUser(teamId string, userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { var webhooks []*model.OutgoingWebhook - query := "" - if limit < 0 || offset < 0 { - query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0" - } else { - query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset" + query := s.getQueryBuilder(). + Select("*"). + From("OutgoingWebhooks"). + Where(sq.And{ + sq.Eq{"TeamId": teamId}, + sq.Eq{"DeleteAt": int(0)}, + }) + + if len(userId) > 0 { + query = query.Where(sq.Eq{"CreatorId": userId}) + } + if limit >= 0 && offset >= 0 { + query = query.Limit(uint64(limit)).Offset(uint64(offset)) } - if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil { + queryString, args, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlWebhookStore.GetOutgoingByTeam", "store.sql_webhooks.get_outgoing_by_team.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + if _, err := s.GetReplica().Select(&webhooks, queryString, args...); err != nil { return nil, model.NewAppError("SqlWebhookStore.GetOutgoingByTeam", "store.sql_webhooks.get_outgoing_by_team.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) } return webhooks, nil } +func (s SqlWebhookStore) GetOutgoingByTeam(teamId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + return s.GetOutgoingByTeamByUser(teamId, "", offset, limit) +} + func (s SqlWebhookStore) DeleteOutgoing(webhookId string, time int64) *model.AppError { _, err := s.GetMaster().Exec("Update OutgoingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": webhookId}) if err != nil { diff --git a/store/store.go b/store/store.go index 4adb8e4a77..671aafc8f3 100644 --- a/store/store.go +++ b/store/store.go @@ -373,7 +373,9 @@ type WebhookStore interface { SaveIncoming(webhook *model.IncomingWebhook) (*model.IncomingWebhook, *model.AppError) GetIncoming(id string, allowFromCache bool) (*model.IncomingWebhook, *model.AppError) GetIncomingList(offset, limit int) ([]*model.IncomingWebhook, *model.AppError) + GetIncomingListByUser(userId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) GetIncomingByTeam(teamId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) + GetIncomingByTeamByUser(teamId string, userId string, offset, limit int) ([]*model.IncomingWebhook, *model.AppError) UpdateIncoming(webhook *model.IncomingWebhook) (*model.IncomingWebhook, *model.AppError) GetIncomingByChannel(channelId string) ([]*model.IncomingWebhook, *model.AppError) DeleteIncoming(webhookId string, time int64) *model.AppError @@ -383,8 +385,11 @@ type WebhookStore interface { SaveOutgoing(webhook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError) GetOutgoing(id string) (*model.OutgoingWebhook, *model.AppError) GetOutgoingByChannel(channelId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) + GetOutgoingByChannelByUser(channelId string, userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) GetOutgoingList(offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) + GetOutgoingListByUser(userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) GetOutgoingByTeam(teamId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) + GetOutgoingByTeamByUser(teamId string, userId string, offset, limit int) ([]*model.OutgoingWebhook, *model.AppError) DeleteOutgoing(webhookId string, time int64) *model.AppError PermanentDeleteOutgoingByChannel(channelId string) *model.AppError PermanentDeleteOutgoingByUser(userId string) *model.AppError diff --git a/store/storetest/mocks/WebhookStore.go b/store/storetest/mocks/WebhookStore.go index 039b6f19dd..f6e3a61e4c 100644 --- a/store/storetest/mocks/WebhookStore.go +++ b/store/storetest/mocks/WebhookStore.go @@ -170,6 +170,31 @@ func (_m *WebhookStore) GetIncomingByTeam(teamId string, offset int, limit int) return r0, r1 } +// GetIncomingByTeamByUser provides a mock function with given fields: teamId, userId, offset, limit +func (_m *WebhookStore) GetIncomingByTeamByUser(teamId string, userId string, offset int, limit int) ([]*model.IncomingWebhook, *model.AppError) { + ret := _m.Called(teamId, userId, offset, limit) + + var r0 []*model.IncomingWebhook + if rf, ok := ret.Get(0).(func(string, string, int, int) []*model.IncomingWebhook); ok { + r0 = rf(teamId, userId, offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.IncomingWebhook) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok { + r1 = rf(teamId, userId, offset, limit) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetIncomingList provides a mock function with given fields: offset, limit func (_m *WebhookStore) GetIncomingList(offset int, limit int) ([]*model.IncomingWebhook, *model.AppError) { ret := _m.Called(offset, limit) @@ -195,6 +220,31 @@ func (_m *WebhookStore) GetIncomingList(offset int, limit int) ([]*model.Incomin return r0, r1 } +// GetIncomingListByUser provides a mock function with given fields: userId, offset, limit +func (_m *WebhookStore) GetIncomingListByUser(userId string, offset int, limit int) ([]*model.IncomingWebhook, *model.AppError) { + ret := _m.Called(userId, offset, limit) + + var r0 []*model.IncomingWebhook + if rf, ok := ret.Get(0).(func(string, int, int) []*model.IncomingWebhook); ok { + r0 = rf(userId, offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.IncomingWebhook) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, int, int) *model.AppError); ok { + r1 = rf(userId, offset, limit) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetOutgoing provides a mock function with given fields: id func (_m *WebhookStore) GetOutgoing(id string) (*model.OutgoingWebhook, *model.AppError) { ret := _m.Called(id) @@ -245,6 +295,31 @@ func (_m *WebhookStore) GetOutgoingByChannel(channelId string, offset int, limit return r0, r1 } +// GetOutgoingByChannelByUser provides a mock function with given fields: channelId, userId, offset, limit +func (_m *WebhookStore) GetOutgoingByChannelByUser(channelId string, userId string, offset int, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + ret := _m.Called(channelId, userId, offset, limit) + + var r0 []*model.OutgoingWebhook + if rf, ok := ret.Get(0).(func(string, string, int, int) []*model.OutgoingWebhook); ok { + r0 = rf(channelId, userId, offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.OutgoingWebhook) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok { + r1 = rf(channelId, userId, offset, limit) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetOutgoingByTeam provides a mock function with given fields: teamId, offset, limit func (_m *WebhookStore) GetOutgoingByTeam(teamId string, offset int, limit int) ([]*model.OutgoingWebhook, *model.AppError) { ret := _m.Called(teamId, offset, limit) @@ -270,6 +345,31 @@ func (_m *WebhookStore) GetOutgoingByTeam(teamId string, offset int, limit int) return r0, r1 } +// GetOutgoingByTeamByUser provides a mock function with given fields: teamId, userId, offset, limit +func (_m *WebhookStore) GetOutgoingByTeamByUser(teamId string, userId string, offset int, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + ret := _m.Called(teamId, userId, offset, limit) + + var r0 []*model.OutgoingWebhook + if rf, ok := ret.Get(0).(func(string, string, int, int) []*model.OutgoingWebhook); ok { + r0 = rf(teamId, userId, offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.OutgoingWebhook) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok { + r1 = rf(teamId, userId, offset, limit) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetOutgoingList provides a mock function with given fields: offset, limit func (_m *WebhookStore) GetOutgoingList(offset int, limit int) ([]*model.OutgoingWebhook, *model.AppError) { ret := _m.Called(offset, limit) @@ -295,6 +395,31 @@ func (_m *WebhookStore) GetOutgoingList(offset int, limit int) ([]*model.Outgoin return r0, r1 } +// GetOutgoingListByUser provides a mock function with given fields: userId, offset, limit +func (_m *WebhookStore) GetOutgoingListByUser(userId string, offset int, limit int) ([]*model.OutgoingWebhook, *model.AppError) { + ret := _m.Called(userId, offset, limit) + + var r0 []*model.OutgoingWebhook + if rf, ok := ret.Get(0).(func(string, int, int) []*model.OutgoingWebhook); ok { + r0 = rf(userId, offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.OutgoingWebhook) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, int, int) *model.AppError); ok { + r1 = rf(userId, offset, limit) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // InvalidateWebhookCache provides a mock function with given fields: webhook func (_m *WebhookStore) InvalidateWebhookCache(webhook string) { _m.Called(webhook) diff --git a/store/storetest/webhook_store.go b/store/storetest/webhook_store.go index 8dff9d1860..d9aff7048b 100644 --- a/store/storetest/webhook_store.go +++ b/store/storetest/webhook_store.go @@ -18,15 +18,20 @@ func TestWebhookStore(t *testing.T, ss store.Store) { t.Run("UpdateIncoming", func(t *testing.T) { testWebhookStoreUpdateIncoming(t, ss) }) t.Run("GetIncoming", func(t *testing.T) { testWebhookStoreGetIncoming(t, ss) }) t.Run("GetIncomingList", func(t *testing.T) { testWebhookStoreGetIncomingList(t, ss) }) + t.Run("GetIncomingListByUser", func(t *testing.T) { testWebhookStoreGetIncomingListByUser(t, ss) }) t.Run("GetIncomingByTeam", func(t *testing.T) { testWebhookStoreGetIncomingByTeam(t, ss) }) + t.Run("GetIncomingByTeamByUser", func(t *testing.T) { TestWebhookStoreGetIncomingByTeamByUser(t, ss) }) t.Run("DeleteIncoming", func(t *testing.T) { testWebhookStoreDeleteIncoming(t, ss) }) t.Run("DeleteIncomingByChannel", func(t *testing.T) { testWebhookStoreDeleteIncomingByChannel(t, ss) }) t.Run("DeleteIncomingByUser", func(t *testing.T) { testWebhookStoreDeleteIncomingByUser(t, ss) }) t.Run("SaveOutgoing", func(t *testing.T) { testWebhookStoreSaveOutgoing(t, ss) }) t.Run("GetOutgoing", func(t *testing.T) { testWebhookStoreGetOutgoing(t, ss) }) t.Run("GetOutgoingList", func(t *testing.T) { testWebhookStoreGetOutgoingList(t, ss) }) + t.Run("GetOutgoingListByUser", func(t *testing.T) { testWebhookStoreGetOutgoingListByUser(t, ss) }) t.Run("GetOutgoingByChannel", func(t *testing.T) { testWebhookStoreGetOutgoingByChannel(t, ss) }) + t.Run("GetOutgoingByChannelByUser", func(t *testing.T) { testWebhookStoreGetOutgoingByChannelByUser(t, ss) }) t.Run("GetOutgoingByTeam", func(t *testing.T) { testWebhookStoreGetOutgoingByTeam(t, ss) }) + t.Run("GetOutgoingByTeamByUser", func(t *testing.T) { testWebhookStoreGetOutgoingByTeamByUser(t, ss) }) t.Run("DeleteOutgoing", func(t *testing.T) { testWebhookStoreDeleteOutgoing(t, ss) }) t.Run("DeleteOutgoingByChannel", func(t *testing.T) { testWebhookStoreDeleteOutgoingByChannel(t, ss) }) t.Run("DeleteOutgoingByUser", func(t *testing.T) { testWebhookStoreDeleteOutgoingByUser(t, ss) }) @@ -144,6 +149,29 @@ func testWebhookStoreGetIncomingList(t *testing.T, ss store.Store) { } } +func testWebhookStoreGetIncomingListByUser(t *testing.T, ss store.Store) { + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1, appErr := ss.Webhook().SaveIncoming(o1) + require.Nil(t, appErr) + + t.Run("GetIncomingListByUser, known user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetIncomingListByUser(o1.UserId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, 1, len(hooks)) + require.Equal(t, o1.CreateAt, hooks[0].CreateAt) + }) + + t.Run("GetIncomingListByUser, unknown user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetIncomingListByUser("123465", 0, 100) + require.Nil(t, appErr) + require.Equal(t, 0, len(hooks)) + }) +} + func testWebhookStoreGetIncomingByTeam(t *testing.T, ss store.Store) { var err *model.AppError @@ -168,6 +196,38 @@ func testWebhookStoreGetIncomingByTeam(t *testing.T, ss store.Store) { } } +func TestWebhookStoreGetIncomingByTeamByUser(t *testing.T, ss store.Store) { + var appErr *model.AppError + + o1 := buildIncomingWebhook() + o1, appErr = ss.Webhook().SaveIncoming(o1) + require.Nil(t, appErr) + + o2 := buildIncomingWebhook() + o2.TeamId = o1.TeamId //Set both to the same team + o2, appErr = ss.Webhook().SaveIncoming(o2) + require.Nil(t, appErr) + + t.Run("GetIncomingByTeamByUser, no user filter", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetIncomingByTeam(o1.TeamId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 2) + }) + + t.Run("GetIncomingByTeamByUser, known user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetIncomingByTeamByUser(o1.TeamId, o1.UserId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 1) + require.Equal(t, hooks[0].CreateAt, o1.CreateAt) + }) + + t.Run("GetIncomingByTeamByUser, unknown user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetIncomingByTeamByUser(o2.TeamId, "123465", 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 0) + }) +} + func testWebhookStoreGetIncomingByChannel(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() @@ -311,6 +371,30 @@ func testWebhookStoreGetOutgoing(t *testing.T, ss store.Store) { } } +func testWebhookStoreGetOutgoingListByUser(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1, appErr := ss.Webhook().SaveOutgoing(o1) + require.Nil(t, appErr) + + t.Run("GetOutgoingListByUser, known user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingListByUser(o1.CreatorId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, 1, len(hooks)) + require.Equal(t, o1.CreateAt, hooks[0].CreateAt) + }) + + t.Run("GetOutgoingListByUser, unknown user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingListByUser("123465", 0, 100) + require.Nil(t, appErr) + require.Equal(t, 0, len(hooks)) + }) +} + func testWebhookStoreGetOutgoingList(t *testing.T, ss store.Store) { o1 := &model.OutgoingWebhook{} o1.ChannelId = model.NewId() @@ -388,6 +472,45 @@ func testWebhookStoreGetOutgoingByChannel(t *testing.T, ss store.Store) { } } +func testWebhookStoreGetOutgoingByChannelByUser(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1, appErr := ss.Webhook().SaveOutgoing(o1) + require.Nil(t, appErr) + + o2 := &model.OutgoingWebhook{} + o2.ChannelId = o1.ChannelId + o2.CreatorId = model.NewId() + o2.TeamId = model.NewId() + o2.CallbackURLs = []string{"http://nowhere.com/"} + + o2, appErr = ss.Webhook().SaveOutgoing(o2) + require.Nil(t, appErr) + + t.Run("GetOutgoingByChannelByUser, no user filter", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByChannel(o1.ChannelId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 2) + }) + + t.Run("GetOutgoingByChannelByUser, known user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByChannelByUser(o1.ChannelId, o1.CreatorId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, 1, len(hooks)) + require.Equal(t, o1.CreateAt, hooks[0].CreateAt) + }) + + t.Run("GetOutgoingByChannelByUser, unknown user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByChannelByUser(o1.ChannelId, "123465", 0, 100) + require.Nil(t, appErr) + require.Equal(t, 0, len(hooks)) + }) +} + func testWebhookStoreGetOutgoingByTeam(t *testing.T, ss store.Store) { o1 := &model.OutgoingWebhook{} o1.ChannelId = model.NewId() @@ -414,6 +537,47 @@ func testWebhookStoreGetOutgoingByTeam(t *testing.T, ss store.Store) { } } +func testWebhookStoreGetOutgoingByTeamByUser(t *testing.T, ss store.Store) { + var appErr *model.AppError + + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1, appErr = ss.Webhook().SaveOutgoing(o1) + require.Nil(t, appErr) + + o2 := &model.OutgoingWebhook{} + o2.ChannelId = model.NewId() + o2.CreatorId = model.NewId() + o2.TeamId = o1.TeamId + o2.CallbackURLs = []string{"http://nowhere.com/"} + + o2, appErr = ss.Webhook().SaveOutgoing(o2) + require.Nil(t, appErr) + + t.Run("GetOutgoingByTeamByUser, no user filter", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByTeam(o1.TeamId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 2) + }) + + t.Run("GetOutgoingByTeamByUser, known user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByTeamByUser(o1.TeamId, o1.CreatorId, 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 1) + require.Equal(t, hooks[0].CreateAt, o1.CreateAt) + }) + + t.Run("GetOutgoingByTeamByUser, unknown user filtered", func(t *testing.T) { + hooks, appErr := ss.Webhook().GetOutgoingByTeamByUser(o2.TeamId, "123465", 0, 100) + require.Nil(t, appErr) + require.Equal(t, len(hooks), 0) + }) +} + func testWebhookStoreDeleteOutgoing(t *testing.T, ss store.Store) { o1 := &model.OutgoingWebhook{} o1.ChannelId = model.NewId()