From 03c6dcbd865e2af2db5db150189504bfa493ae2e Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Sun, 15 Nov 2015 18:18:02 -0800 Subject: PLT-975 adding perm deletes --- store/sql_audit_store.go | 19 +++++++++++++ store/sql_audit_store_test.go | 4 +++ store/sql_channel_store.go | 17 ++++++++++++ store/sql_channel_store_test.go | 57 ++++++++++++++++++++++++++++++++++++++ store/sql_oauth_store.go | 18 ++++++++++++ store/sql_oauth_store_test.go | 14 ++++++++++ store/sql_post_store.go | 18 ++++++++++++ store/sql_preference_store.go | 18 ++++++++++++ store/sql_preference_store_test.go | 40 ++++++++++++++++++++++++++ store/sql_session_store.go | 18 ++++++++++++ store/sql_session_store_test.go | 23 +++++++++++++++ store/sql_team_store.go | 17 ++++++++++++ store/sql_team_store_test.go | 23 +++++++++++++++ store/sql_user_store.go | 18 ++++++++++++ store/sql_user_store_test.go | 13 +++++++++ store/sql_webhook_store.go | 36 ++++++++++++++++++++++++ store/sql_webhook_store_test.go | 57 ++++++++++++++++++++++++++++++++++++++ store/store.go | 9 ++++++ 18 files changed, 419 insertions(+) diff --git a/store/sql_audit_store.go b/store/sql_audit_store.go index b3e2daea0..fd39eda8f 100644 --- a/store/sql_audit_store.go +++ b/store/sql_audit_store.go @@ -86,3 +86,22 @@ func (s SqlAuditStore) Get(user_id string, limit int) StoreChannel { return storeChannel } + +func (s SqlAuditStore) Delete(userId string) StoreChannel { + + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := s.GetMaster().Exec("DELETE FROM Audits WHERE UserId = :userId", + map[string]interface{}{"userId": userId}); err != nil { + result.Err = model.NewAppError("SqlAuditStore.Delete", "We encountered an error deleting the audits", "user_id="+userId) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_audit_store_test.go b/store/sql_audit_store_test.go index e265db837..4dce6701f 100644 --- a/store/sql_audit_store_test.go +++ b/store/sql_audit_store_test.go @@ -44,4 +44,8 @@ func TestSqlAuditStore(t *testing.T) { if len(audits) != 0 { t.Fatal("Should have returned empty because user_id is missing") } + + if r2 := <-store.Audit().Delete(audit.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } } diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index a9f99bd67..65e2cc45d 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -616,6 +616,23 @@ func (s SqlChannelStore) RemoveMember(channelId string, userId string) StoreChan return storeChannel } +func (s SqlChannelStore) DeleteMember(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "We couldn't remove the channel member", "user_id="+userId+", "+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlChannelStore) CheckPermissionsTo(teamId string, channelId string, userId string) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go index 8662fcbd3..238f79be8 100644 --- a/store/sql_channel_store_test.go +++ b/store/sql_channel_store_test.go @@ -379,6 +379,63 @@ func TestChannelMemberStore(t *testing.T) { } } +func TestChannelDeleteMemberStore(t *testing.T) { + Setup() + + c1 := model.Channel{} + c1.TeamId = model.NewId() + c1.DisplayName = "NameName" + c1.Name = "a" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = *Must(store.Channel().Save(&c1)).(*model.Channel) + + c1t1 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + t1 := c1t1.ExtraUpdateAt + + u1 := model.User{} + u1.TeamId = model.NewId() + u1.Email = model.NewId() + u1.Nickname = model.NewId() + Must(store.User().Save(&u1)) + + u2 := model.User{} + u2.TeamId = model.NewId() + u2.Email = model.NewId() + u2.Nickname = model.NewId() + Must(store.User().Save(&u2)) + + o1 := model.ChannelMember{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.NotifyProps = model.GetDefaultChannelNotifyProps() + Must(store.Channel().SaveMember(&o1)) + + o2 := model.ChannelMember{} + o2.ChannelId = c1.Id + o2.UserId = u2.Id + o2.NotifyProps = model.GetDefaultChannelNotifyProps() + Must(store.Channel().SaveMember(&o2)) + + c1t2 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + t2 := c1t2.ExtraUpdateAt + + if t2 <= t1 { + t.Fatal("Member update time incorrect") + } + + count := (<-store.Channel().GetMemberCount(o1.ChannelId)).Data.(int64) + if count != 2 { + t.Fatal("should have saved 2 members") + } + + Must(store.Channel().DeleteMember(o2.UserId)) + + count = (<-store.Channel().GetMemberCount(o1.ChannelId)).Data.(int64) + if count != 1 { + t.Fatal("should have removed 1 member") + } +} + func TestChannelStorePermissionsTo(t *testing.T) { Setup() diff --git a/store/sql_oauth_store.go b/store/sql_oauth_store.go index 751207b85..688e39b09 100644 --- a/store/sql_oauth_store.go +++ b/store/sql_oauth_store.go @@ -332,3 +332,21 @@ func (as SqlOAuthStore) RemoveAuthData(code string) StoreChannel { return storeChannel } + +func (as SqlOAuthStore) RemoveAuthDataByUserId(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := as.GetMaster().Exec("DELETE FROM OAuthAuthData WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) + if err != nil { + result.Err = model.NewAppError("SqlOAuthStore.RemoveAuthDataByUserId", "We couldn't remove the authorization code", "err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_oauth_store_test.go b/store/sql_oauth_store_test.go index 3f05f1c92..2a9aad1b3 100644 --- a/store/sql_oauth_store_test.go +++ b/store/sql_oauth_store_test.go @@ -180,3 +180,17 @@ func TestOAuthStoreRemoveAuthData(t *testing.T) { t.Fatal("should have errored - auth code removed") } } + +func TestOAuthStoreRemoveAuthDataByUserId(t *testing.T) { + Setup() + + a1 := model.AuthData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Code = model.NewId() + Must(store.OAuth().SaveAuthData(&a1)) + + if err := (<-store.OAuth().RemoveAuthDataByUserId(a1.UserId)).Err; err != nil { + t.Fatal(err) + } +} diff --git a/store/sql_post_store.go b/store/sql_post_store.go index f800367cb..0bcb420bd 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -228,6 +228,24 @@ func (s SqlPostStore) Delete(postId string, time int64) StoreChannel { return storeChannel } +func (s SqlPostStore) PermanentDelete(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("Update Posts SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id OR ParentId = :ParentId OR RootId = :RootId", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": postId, "ParentId": postId, "RootId": postId}) + if err != nil { + result.Err = model.NewAppError("SqlPostStore.Delete", "We couldn't delete the post", "id="+postId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlPostStore) GetPosts(channelId string, offset int, limit int) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/sql_preference_store.go b/store/sql_preference_store.go index f9f38b747..6fa30ba7a 100644 --- a/store/sql_preference_store.go +++ b/store/sql_preference_store.go @@ -239,3 +239,21 @@ func (s SqlPreferenceStore) GetAll(userId string) StoreChannel { return storeChannel } + +func (s SqlPreferenceStore) Delete(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := s.GetMaster().Exec( + `DELETE FROM Preferences WHERE UserId = :UserId`, map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewAppError("SqlPreferenceStore.Delete", "We encountered an error while deleteing preferences", err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_preference_store_test.go b/store/sql_preference_store_test.go index e68203cc3..8d850e22c 100644 --- a/store/sql_preference_store_test.go +++ b/store/sql_preference_store_test.go @@ -192,3 +192,43 @@ func TestPreferenceGetAll(t *testing.T) { } } } + +func TestPreferenceDelete(t *testing.T) { + Setup() + + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + // same name/category, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + Must(store.Preference().Save(&preferences)) + + if result := <-store.Preference().Delete(userId); result.Err != nil { + t.Fatal(result.Err) + } +} diff --git a/store/sql_session_store.go b/store/sql_session_store.go index 27b34ee39..8237d8200 100644 --- a/store/sql_session_store.go +++ b/store/sql_session_store.go @@ -158,6 +158,24 @@ func (me SqlSessionStore) RemoveAllSessionsForTeam(teamId string) StoreChannel { return storeChannel } +func (me SqlSessionStore) RemoveAllSessionsForUser(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) + if err != nil { + result.Err = model.NewAppError("SqlSessionStore.RemoveAllSessionsForUser", "We couldn't remove all the sessions for the user", "id="+userId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (me SqlSessionStore) CleanUpExpiredSessions(userId string) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/sql_session_store_test.go b/store/sql_session_store_test.go index 068e5fc19..a06a2bdff 100644 --- a/store/sql_session_store_test.go +++ b/store/sql_session_store_test.go @@ -103,6 +103,29 @@ func TestSessionRemoveAll(t *testing.T) { } } +func TestSessionRemoveByUser(t *testing.T) { + Setup() + + s1 := model.Session{} + s1.UserId = model.NewId() + s1.TeamId = model.NewId() + Must(store.Session().Save(&s1)) + + if rs1 := (<-store.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + Must(store.Session().RemoveAllSessionsForUser(s1.UserId)) + + if rs2 := (<-store.Session().Get(s1.Id)); rs2.Err == nil { + t.Fatal("should have been removed") + } +} + func TestSessionRemoveToken(t *testing.T) { Setup() diff --git a/store/sql_team_store.go b/store/sql_team_store.go index 1a0aeabde..7bcdc7349 100644 --- a/store/sql_team_store.go +++ b/store/sql_team_store.go @@ -300,3 +300,20 @@ func (s SqlTeamStore) GetAllTeamListing() StoreChannel { return storeChannel } + +func (s SqlTeamStore) Delete(teamId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := s.GetMaster().Exec("DELETE FROM Teams WHERE Id = :TeamId", map[string]interface{}{"TeamId": teamId}); err != nil { + result.Err = model.NewAppError("SqlTeamStore.Delete", "We couldn't delete the existing team", "teamId="+teamId+", "+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_team_store_test.go b/store/sql_team_store_test.go index 71740f7e7..6547b0c8f 100644 --- a/store/sql_team_store_test.go +++ b/store/sql_team_store_test.go @@ -238,3 +238,26 @@ func TestAllTeamListing(t *testing.T) { } } } + +func TestDelete(t *testing.T) { + Setup() + + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowTeamListing = true + Must(store.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "a" + model.NewId() + "b" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + Must(store.Team().Save(&o2)) + + if r1 := <-store.Team().Delete(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } +} diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 77ff5bfab..90efc00f1 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -554,3 +554,21 @@ func (us SqlUserStore) GetTotalActiveUsersCount() StoreChannel { return storeChannel } + +func (us SqlUserStore) Delete(userId string) StoreChannel { + + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := us.GetMaster().Exec("DELETE FROM Users WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewAppError("SqlUserStore.GetByEmail", "We couldn't delete the existing account", "userId="+userId+", "+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go index 874baf634..25f4a3475 100644 --- a/store/sql_user_store_test.go +++ b/store/sql_user_store_test.go @@ -377,3 +377,16 @@ func TestUserStoreUpdatePassword(t *testing.T) { } } } + +func TestUserStoreDelete(t *testing.T) { + Setup() + + u1 := model.User{} + u1.TeamId = model.NewId() + u1.Email = model.NewId() + Must(store.User().Save(&u1)) + + if err := (<-store.User().Delete(u1.Id)).Err; err != nil { + t.Fatal(err) + } +} diff --git a/store/sql_webhook_store.go b/store/sql_webhook_store.go index c758e2339..11b9f6e52 100644 --- a/store/sql_webhook_store.go +++ b/store/sql_webhook_store.go @@ -116,6 +116,24 @@ func (s SqlWebhookStore) DeleteIncoming(webhookId string, time int64) StoreChann return storeChannel } +func (s SqlWebhookStore) DeleteIncomingByUser(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("DELETE FROM IncomingWebhooks WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) + if err != nil { + result.Err = model.NewAppError("SqlWebhookStore.DeleteIncomingByUser", "We couldn't delete the webhook", "id="+userId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel { storeChannel := make(StoreChannel) @@ -294,6 +312,24 @@ func (s SqlWebhookStore) DeleteOutgoing(webhookId string, time int64) StoreChann return storeChannel } +func (s SqlWebhookStore) DeleteOutgoingByUser(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + _, err := s.GetMaster().Exec("DELETE FROM OutgoingWebhooks WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId}) + if err != nil { + result.Err = model.NewAppError("SqlWebhookStore.DeleteOutgoingByUser", "We couldn't delete the webhook", "id="+userId+", err="+err.Error()) + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlWebhookStore) UpdateOutgoing(hook *model.OutgoingWebhook) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/sql_webhook_store_test.go b/store/sql_webhook_store_test.go index 1fb990f3e..1b770c6d4 100644 --- a/store/sql_webhook_store_test.go +++ b/store/sql_webhook_store_test.go @@ -103,6 +103,34 @@ func TestWebhookStoreDeleteIncoming(t *testing.T) { } } +func TestWebhookStoreDeleteIncomingByUser(t *testing.T) { + Setup() + + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-store.Webhook().GetIncoming(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-store.Webhook().DeleteIncomingByUser(o1.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-store.Webhook().GetIncoming(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + func TestWebhookStoreSaveOutgoing(t *testing.T) { Setup() @@ -258,6 +286,35 @@ func TestWebhookStoreDeleteOutgoing(t *testing.T) { } } +func TestWebhookStoreDeleteOutgoingByUser(t *testing.T) { + Setup() + + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-store.Webhook().GetOutgoing(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.OutgoingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-store.Webhook().DeleteOutgoingByUser(o1.CreatorId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-store.Webhook().GetOutgoing(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + func TestWebhookStoreUpdateOutgoing(t *testing.T) { Setup() diff --git a/store/store.go b/store/store.go index 13b59b582..13434e7b0 100644 --- a/store/store.go +++ b/store/store.go @@ -52,6 +52,7 @@ type TeamStore interface { GetAll() StoreChannel GetAllTeamListing() StoreChannel GetByInviteId(inviteId string) StoreChannel + Delete(teamId string) StoreChannel } type ChannelStore interface { @@ -72,6 +73,7 @@ type ChannelStore interface { GetMember(channelId string, userId string) StoreChannel GetMemberCount(channelId string) StoreChannel RemoveMember(channelId string, userId string) StoreChannel + DeleteMember(userId string) StoreChannel GetExtraMembers(channelId string, limit int) StoreChannel CheckPermissionsTo(teamId string, channelId string, userId string) StoreChannel CheckOpenChannelPermissions(teamId string, channelId string) StoreChannel @@ -118,6 +120,7 @@ type UserStore interface { GetTotalUsersCount() StoreChannel GetTotalActiveUsersCount() StoreChannel GetSystemAdminProfiles() StoreChannel + Delete(userId string) StoreChannel } type SessionStore interface { @@ -126,6 +129,7 @@ type SessionStore interface { GetSessions(userId string) StoreChannel Remove(sessionIdOrToken string) StoreChannel RemoveAllSessionsForTeam(teamId string) StoreChannel + RemoveAllSessionsForUser(teamId string) StoreChannel UpdateLastActivityAt(sessionId string, time int64) StoreChannel UpdateRoles(userId string, roles string) StoreChannel } @@ -133,6 +137,7 @@ type SessionStore interface { type AuditStore interface { Save(audit *model.Audit) StoreChannel Get(user_id string, limit int) StoreChannel + Delete(userId string) StoreChannel } type OAuthStore interface { @@ -143,6 +148,7 @@ type OAuthStore interface { SaveAuthData(authData *model.AuthData) StoreChannel GetAuthData(code string) StoreChannel RemoveAuthData(code string) StoreChannel + RemoveAuthDataByUserId(userId string) StoreChannel SaveAccessData(accessData *model.AccessData) StoreChannel GetAccessData(token string) StoreChannel GetAccessDataByAuthCode(authCode string) StoreChannel @@ -161,12 +167,14 @@ type WebhookStore interface { GetIncomingByUser(userId string) StoreChannel GetIncomingByChannel(channelId string) StoreChannel DeleteIncoming(webhookId string, time int64) StoreChannel + DeleteIncomingByUser(userId string) StoreChannel SaveOutgoing(webhook *model.OutgoingWebhook) StoreChannel GetOutgoing(id string) StoreChannel GetOutgoingByCreator(userId string) StoreChannel GetOutgoingByChannel(channelId string) StoreChannel GetOutgoingByTeam(teamId string) StoreChannel DeleteOutgoing(webhookId string, time int64) StoreChannel + DeleteOutgoingByUser(userId string) StoreChannel UpdateOutgoing(hook *model.OutgoingWebhook) StoreChannel } @@ -175,4 +183,5 @@ type PreferenceStore interface { Get(userId string, category string, name string) StoreChannel GetCategory(userId string, category string) StoreChannel GetAll(userId string) StoreChannel + Delete(userId string) StoreChannel } -- cgit v1.2.3-1-g7c22