From a09dc68e1d99394f5d636284e0580dd17b2773b3 Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Thu, 17 May 2018 16:28:14 +0100 Subject: MM-10235: Make permissions reset CLI shcemes-aware. (#8773) * MM-10235: Make permissions reset CLI shcemes-aware. * Add i18n strings. --- app/permissions.go | 15 ++++++++ i18n/en.json | 12 +++++++ store/layered_store.go | 6 ++++ store/layered_store_supplier.go | 1 + store/local_cache_supplier_schemes.go | 6 ++++ store/redis_supplier_schemes.go | 5 +++ store/sqlstore/channel_store.go | 8 +++++ store/sqlstore/scheme_supplier.go | 10 ++++++ store/sqlstore/team_store.go | 8 +++++ store/store.go | 3 ++ store/storetest/channel_store.go | 41 ++++++++++++++++++++++ store/storetest/mocks/ChannelStore.go | 16 +++++++++ store/storetest/mocks/LayeredStoreDatabaseLayer.go | 23 ++++++++++++ store/storetest/mocks/LayeredStoreSupplier.go | 23 ++++++++++++ store/storetest/mocks/SchemeStore.go | 16 +++++++++ store/storetest/mocks/TeamStore.go | 16 +++++++++ store/storetest/scheme_store.go | 31 ++++++++++++++++ store/storetest/team_store.go | 41 ++++++++++++++++++++++ 18 files changed, 281 insertions(+) diff --git a/app/permissions.go b/app/permissions.go index be975e03d..75aa2ecf9 100644 --- a/app/permissions.go +++ b/app/permissions.go @@ -8,6 +8,21 @@ import ( ) func (a *App) ResetPermissionsSystem() *model.AppError { + // Reset all Teams to not have a scheme. + if result := <-a.Srv.Store.Team().ResetAllTeamSchemes(); result.Err != nil { + return result.Err + } + + // Reset all Channels to not have a scheme. + if result := <-a.Srv.Store.Channel().ResetAllChannelSchemes(); result.Err != nil { + return result.Err + } + + // Purge all schemes from the database. + if result := <-a.Srv.Store.Scheme().PermanentDeleteAll(); result.Err != nil { + return result.Err + } + // Purge all roles from the database. if result := <-a.Srv.Store.Role().PermanentDeleteAll(); result.Err != nil { return result.Err diff --git a/i18n/en.json b/i18n/en.json index 5dc05eba7..8d2c90db0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -275,6 +275,18 @@ "id": "api.channel.convert_channel_to_private.private_channel_error", "translation": "The channel requested to convert is already a private channel." }, + { + "id": "store.sql_channel.reset_all_channel_schemes.app_error", + "translation": "We could not reset the channel schemes" + }, + { + "id": "store.sql_scheme.permanent_delete_all.app_error", + "translation": "We could not permanently delete the schemes" + }, + { + "id": "store.sql_team.reset_all_team_schemes.app_error", + "translation": "We could not reset the team schemes" + }, { "id": "api.channel.create_channel.direct_channel.app_error", "translation": "Must use createDirectChannel API service for direct message channel creation" diff --git a/store/layered_store.go b/store/layered_store.go index cbabe9d22..69513febf 100644 --- a/store/layered_store.go +++ b/store/layered_store.go @@ -298,3 +298,9 @@ func (s *LayeredSchemeStore) GetAllPage(scope string, offset int, limit int) Sto return supplier.SchemeGetAllPage(s.TmpContext, scope, offset, limit) }) } + +func (s *LayeredSchemeStore) PermanentDeleteAll() StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.SchemePermanentDeleteAll(s.TmpContext) + }) +} diff --git a/store/layered_store_supplier.go b/store/layered_store_supplier.go index 4f57004bb..6bf4a0310 100644 --- a/store/layered_store_supplier.go +++ b/store/layered_store_supplier.go @@ -43,4 +43,5 @@ type LayeredStoreSupplier interface { SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult } diff --git a/store/local_cache_supplier_schemes.go b/store/local_cache_supplier_schemes.go index 809c60510..b6cde0fc4 100644 --- a/store/local_cache_supplier_schemes.go +++ b/store/local_cache_supplier_schemes.go @@ -46,3 +46,9 @@ func (s *LocalCacheSupplier) SchemeDelete(ctx context.Context, schemeId string, func (s *LocalCacheSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...) } + +func (s *LocalCacheSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + defer s.doClearCacheCluster(s.schemeCache) + + return s.Next().SchemePermanentDeleteAll(ctx, hints...) +} diff --git a/store/redis_supplier_schemes.go b/store/redis_supplier_schemes.go index 3bd747044..1af9dafde 100644 --- a/store/redis_supplier_schemes.go +++ b/store/redis_supplier_schemes.go @@ -28,3 +28,8 @@ func (s *RedisSupplier) SchemeGetAllPage(ctx context.Context, scope string, offs // TODO: Redis caching. return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...) } + +func (s *RedisSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis caching. + return s.Next().SchemePermanentDeleteAll(ctx, hints...) +} diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index 4948d0995..5f336d904 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -1772,3 +1772,11 @@ func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId result.Data = data }) } + +func (s SqlChannelStore) ResetAllChannelSchemes() store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + if _, err := s.GetMaster().Exec("UPDATE Channels SET SchemeId=''"); err != nil { + result.Err = model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.app_error", nil, err.Error(), http.StatusInternalServerError) + } + }) +} diff --git a/store/sqlstore/scheme_supplier.go b/store/sqlstore/scheme_supplier.go index 233d2f660..e15bb3629 100644 --- a/store/sqlstore/scheme_supplier.go +++ b/store/sqlstore/scheme_supplier.go @@ -282,3 +282,13 @@ func (s *SqlSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset return result } + +func (s *SqlSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + if _, err := s.GetMaster().Exec("DELETE from Schemes"); err != nil { + result.Err = model.NewAppError("SqlSchemeStore.PermanentDeleteAll", "store.sql_scheme.permanent_delete_all.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + return result +} diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index ea5f7fd1f..f8d76bba1 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -794,3 +794,11 @@ func (s SqlTeamStore) MigrateTeamMembers(fromTeamId string, fromUserId string) s result.Data = data }) } + +func (s SqlTeamStore) ResetAllTeamSchemes() store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + if _, err := s.GetMaster().Exec("UPDATE Teams SET SchemeId=''"); err != nil { + result.Err = model.NewAppError("SqlTeamStore.ResetAllTeamSchemes", "store.sql_team.reset_all_team_schemes.app_error", nil, err.Error(), http.StatusInternalServerError) + } + }) +} diff --git a/store/store.go b/store/store.go index bf2ac42f5..bfc0ab845 100644 --- a/store/store.go +++ b/store/store.go @@ -106,6 +106,7 @@ type TeamStore interface { UpdateLastTeamIconUpdate(teamId string, curTime int64) StoreChannel GetTeamsByScheme(schemeId string, offset int, limit int) StoreChannel MigrateTeamMembers(fromTeamId string, fromUserId string) StoreChannel + ResetAllTeamSchemes() StoreChannel } type ChannelStore interface { @@ -165,6 +166,7 @@ type ChannelStore interface { ClearCaches() GetChannelsByScheme(schemeId string, offset int, limit int) StoreChannel MigrateChannelMembers(fromChannelId string, fromUserId string) StoreChannel + ResetAllChannelSchemes() StoreChannel } type ChannelMemberHistoryStore interface { @@ -489,4 +491,5 @@ type SchemeStore interface { Get(schemeId string) StoreChannel GetAllPage(scope string, offset int, limit int) StoreChannel Delete(schemeId string) StoreChannel + PermanentDeleteAll() StoreChannel } diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index 6e0d30a2d..21db7eb91 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -54,6 +54,7 @@ func TestChannelStore(t *testing.T, ss store.Store) { t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) }) t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) }) t.Run("MigrateChannelMembers", func(t *testing.T) { testChannelStoreMigrateChannelMembers(t, ss) }) + t.Run("ResetAllChannelSchemes", func(t *testing.T) { testResetAllChannelSchemes(t, ss) }) } @@ -2296,3 +2297,43 @@ func testChannelStoreMigrateChannelMembers(t *testing.T, ss store.Store) { assert.False(t, cm3b.SchemeUser) assert.False(t, cm3b.SchemeAdmin) } + +func testResetAllChannelSchemes(t *testing.T, ss store.Store) { + s1 := &model.Scheme{ + Name: model.NewId(), + Description: model.NewId(), + Scope: model.SCHEME_SCOPE_CHANNEL, + } + s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme) + + c1 := &model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: model.NewId(), + Type: model.CHANNEL_OPEN, + SchemeId: &s1.Id, + } + + c2 := &model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: model.NewId(), + Type: model.CHANNEL_OPEN, + SchemeId: &s1.Id, + } + + c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel) + c2 = (<-ss.Channel().Save(c2, 100)).Data.(*model.Channel) + + assert.Equal(t, s1.Id, *c1.SchemeId) + assert.Equal(t, s1.Id, *c2.SchemeId) + + res := <-ss.Channel().ResetAllChannelSchemes() + assert.Nil(t, res.Err) + + c1 = (<-ss.Channel().Get(c1.Id, true)).Data.(*model.Channel) + c2 = (<-ss.Channel().Get(c2.Id, true)).Data.(*model.Channel) + + assert.Equal(t, "", *c1.SchemeId) + assert.Equal(t, "", *c2.SchemeId) +} diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index 8858e3d3b..10ac908e4 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -679,6 +679,22 @@ func (_m *ChannelStore) RemoveMember(channelId string, userId string) store.Stor return r0 } +// ResetAllChannelSchemes provides a mock function with given fields: +func (_m *ChannelStore) ResetAllChannelSchemes() store.StoreChannel { + ret := _m.Called() + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func() store.StoreChannel); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // Restore provides a mock function with given fields: channelId, time func (_m *ChannelStore) Restore(channelId string, time int64) store.StoreChannel { ret := _m.Called(channelId, time) diff --git a/store/storetest/mocks/LayeredStoreDatabaseLayer.go b/store/storetest/mocks/LayeredStoreDatabaseLayer.go index e6b8bafb1..c5b821b05 100644 --- a/store/storetest/mocks/LayeredStoreDatabaseLayer.go +++ b/store/storetest/mocks/LayeredStoreDatabaseLayer.go @@ -655,6 +655,29 @@ func (_m *LayeredStoreDatabaseLayer) SchemeGetAllPage(ctx context.Context, scope return r0 } +// SchemePermanentDeleteAll provides a mock function with given fields: ctx, hints +func (_m *LayeredStoreDatabaseLayer) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // SchemeSave provides a mock function with given fields: ctx, scheme, hints func (_m *LayeredStoreDatabaseLayer) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { _va := make([]interface{}, len(hints)) diff --git a/store/storetest/mocks/LayeredStoreSupplier.go b/store/storetest/mocks/LayeredStoreSupplier.go index ef6fd99e2..37a01df14 100644 --- a/store/storetest/mocks/LayeredStoreSupplier.go +++ b/store/storetest/mocks/LayeredStoreSupplier.go @@ -352,6 +352,29 @@ func (_m *LayeredStoreSupplier) SchemeGetAllPage(ctx context.Context, scope stri return r0 } +// SchemePermanentDeleteAll provides a mock function with given fields: ctx, hints +func (_m *LayeredStoreSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // SchemeSave provides a mock function with given fields: ctx, scheme, hints func (_m *LayeredStoreSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { _va := make([]interface{}, len(hints)) diff --git a/store/storetest/mocks/SchemeStore.go b/store/storetest/mocks/SchemeStore.go index 2868521b3..ffb10f931 100644 --- a/store/storetest/mocks/SchemeStore.go +++ b/store/storetest/mocks/SchemeStore.go @@ -61,6 +61,22 @@ func (_m *SchemeStore) GetAllPage(scope string, offset int, limit int) store.Sto return r0 } +// PermanentDeleteAll provides a mock function with given fields: +func (_m *SchemeStore) PermanentDeleteAll() store.StoreChannel { + ret := _m.Called() + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func() store.StoreChannel); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // Save provides a mock function with given fields: scheme func (_m *SchemeStore) Save(scheme *model.Scheme) store.StoreChannel { ret := _m.Called(scheme) diff --git a/store/storetest/mocks/TeamStore.go b/store/storetest/mocks/TeamStore.go index 93cb84caf..ef5529a1f 100644 --- a/store/storetest/mocks/TeamStore.go +++ b/store/storetest/mocks/TeamStore.go @@ -381,6 +381,22 @@ func (_m *TeamStore) RemoveMember(teamId string, userId string) store.StoreChann return r0 } +// ResetAllTeamSchemes provides a mock function with given fields: +func (_m *TeamStore) ResetAllTeamSchemes() store.StoreChannel { + ret := _m.Called() + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func() store.StoreChannel); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // Save provides a mock function with given fields: team func (_m *TeamStore) Save(team *model.Team) store.StoreChannel { ret := _m.Called(team) diff --git a/store/storetest/scheme_store.go b/store/storetest/scheme_store.go index 49bc92bb6..636000953 100644 --- a/store/storetest/scheme_store.go +++ b/store/storetest/scheme_store.go @@ -19,6 +19,7 @@ func TestSchemeStore(t *testing.T, ss store.Store) { t.Run("Get", func(t *testing.T) { testSchemeStoreGet(t, ss) }) t.Run("GetAllPage", func(t *testing.T) { testSchemeStoreGetAllPage(t, ss) }) t.Run("Delete", func(t *testing.T) { testSchemeStoreDelete(t, ss) }) + t.Run("PermanentDeleteAll", func(t *testing.T) { testSchemeStorePermanentDeleteAll(t, ss) }) } func createDefaultRoles(t *testing.T, ss store.Store) { @@ -372,3 +373,33 @@ func testSchemeStoreDelete(t *testing.T, ss store.Store) { c6 := cres6.Data.(*model.Channel) assert.Equal(t, "", *c6.SchemeId) } + +func testSchemeStorePermanentDeleteAll(t *testing.T, ss store.Store) { + s1 := &model.Scheme{ + Name: model.NewId(), + Description: model.NewId(), + Scope: model.SCHEME_SCOPE_TEAM, + } + + s2 := &model.Scheme{ + Name: model.NewId(), + Description: model.NewId(), + Scope: model.SCHEME_SCOPE_CHANNEL, + } + + s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme) + s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme) + + res := <-ss.Scheme().PermanentDeleteAll() + assert.Nil(t, res.Err) + + res1 := <-ss.Scheme().Get(s1.Id) + assert.NotNil(t, res1.Err) + + res2 := <-ss.Scheme().Get(s2.Id) + assert.NotNil(t, res2.Err) + + res3 := <-ss.Scheme().GetAllPage("", 0, 100000) + assert.Nil(t, res3.Err) + assert.Len(t, res3.Data.([]*model.Scheme), 0) +} diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go index 726c17a99..996f8fd8f 100644 --- a/store/storetest/team_store.go +++ b/store/storetest/team_store.go @@ -41,6 +41,7 @@ func TestTeamStore(t *testing.T, ss store.Store) { t.Run("UpdateLastTeamIconUpdate", func(t *testing.T) { testUpdateLastTeamIconUpdate(t, ss) }) t.Run("GetTeamsByScheme", func(t *testing.T) { testGetTeamsByScheme(t, ss) }) t.Run("MigrateTeamMembers", func(t *testing.T) { testTeamStoreMigrateTeamMembers(t, ss) }) + t.Run("ResetAllTeamSchemes", func(t *testing.T) { testResetAllTeamSchemes(t, ss) }) } func testTeamStoreSave(t *testing.T, ss store.Store) { @@ -1169,3 +1170,43 @@ func testTeamStoreMigrateTeamMembers(t *testing.T, ss store.Store) { assert.False(t, tm3b.SchemeUser) assert.False(t, tm3b.SchemeAdmin) } + +func testResetAllTeamSchemes(t *testing.T, ss store.Store) { + s1 := &model.Scheme{ + Name: model.NewId(), + Description: model.NewId(), + Scope: model.SCHEME_SCOPE_TEAM, + } + s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme) + + t1 := &model.Team{ + Name: model.NewId(), + DisplayName: model.NewId(), + Email: model.NewId() + "@nowhere.com", + Type: model.TEAM_OPEN, + SchemeId: &s1.Id, + } + + t2 := &model.Team{ + Name: model.NewId(), + DisplayName: model.NewId(), + Email: model.NewId() + "@nowhere.com", + Type: model.TEAM_OPEN, + SchemeId: &s1.Id, + } + + t1 = (<-ss.Team().Save(t1)).Data.(*model.Team) + t2 = (<-ss.Team().Save(t2)).Data.(*model.Team) + + assert.Equal(t, s1.Id, *t1.SchemeId) + assert.Equal(t, s1.Id, *t2.SchemeId) + + res := <-ss.Team().ResetAllTeamSchemes() + assert.Nil(t, res.Err) + + t1 = (<-ss.Team().Get(t1.Id)).Data.(*model.Team) + t2 = (<-ss.Team().Get(t2.Id)).Data.(*model.Team) + + assert.Equal(t, "", *t1.SchemeId) + assert.Equal(t, "", *t2.SchemeId) +} -- cgit v1.2.3-1-g7c22