diff --git a/server/channels/app/role.go b/server/channels/app/role.go index c678f92790..f3e3b3dc54 100644 --- a/server/channels/app/role.go +++ b/server/channels/app/role.go @@ -7,12 +7,14 @@ import ( "context" "encoding/json" "errors" + "fmt" "net/http" "reflect" "slices" "strings" "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/utils" @@ -275,13 +277,93 @@ func (a *App) CheckRolesExist(roleNames []string) *model.AppError { } func (a *App) sendUpdatedRoleEvent(role *model.Role) *model.AppError { - message := model.NewWebSocketEvent(model.WebsocketEventRoleUpdated, "", "", "", nil, "") roleJSON, jsonErr := json.Marshal(role) if jsonErr != nil { return model.NewAppError("sendUpdatedRoleEvent", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr) } - message.Add("role", string(roleJSON)) - a.Publish(message) + + publishEvent := func(teamID, channelID string) { + message := model.NewWebSocketEvent(model.WebsocketEventRoleUpdated, teamID, channelID, "", nil, "") + message.Add("role", string(roleJSON)) + a.Publish(message) + } + + // Built-in system roles apply to all users; broadcast globally without a DB lookup. + if role.BuiltIn { + publishEvent("", "") + return nil + } + + // Scheme-managed roles: use SchemeId to look up the owning scheme. + if role.SchemeId == nil { + // No owning scheme — treat as global (e.g. custom non-scheme role). + publishEvent("", "") + return nil + } + scheme, err := a.Srv().Store().Scheme().Get(*role.SchemeId) + if err != nil { + a.Log().Error("Failed to look up scheme for role event; skipping broadcast", + mlog.String("role_id", role.Id), + mlog.String("scheme_id", *role.SchemeId), + mlog.Err(err)) + return nil + } + + const pageSize = 1000 + const maxBroadcasts = 100000 + switch scheme.Scope { + case model.SchemeScopeTeam: + totalBroadcasts := 0 + offset := 0 + for { + teams, storeErr := a.Srv().Store().Team().GetTeamsByScheme(scheme.Id, offset, pageSize) + if storeErr != nil { + return model.NewAppError("sendUpdatedRoleEvent", "app.role.send_updated_role_event.app_error", nil, "", http.StatusInternalServerError).Wrap(storeErr) + } + for _, team := range teams { + publishEvent(team.Id, "") + } + totalBroadcasts += len(teams) + if len(teams) < pageSize { + break + } + if totalBroadcasts >= maxBroadcasts { + a.Log().Error("sendUpdatedRoleEvent: hit broadcast limit for team scheme", + mlog.String("scheme_id", scheme.Id), + mlog.Int("totalBroadcasts", totalBroadcasts)) + break + } + offset += pageSize + } + case model.SchemeScopeChannel: + totalBroadcasts := 0 + offset := 0 + for { + channels, storeErr := a.Srv().Store().Channel().GetChannelsByScheme(scheme.Id, offset, pageSize) + if storeErr != nil { + return model.NewAppError("sendUpdatedRoleEvent", "app.role.send_updated_role_event.app_error", nil, "", http.StatusInternalServerError).Wrap(storeErr) + } + for _, channel := range channels { + publishEvent("", channel.Id) + } + totalBroadcasts += len(channels) + if len(channels) < pageSize { + break + } + if totalBroadcasts >= maxBroadcasts { + a.Log().Error("sendUpdatedRoleEvent: hit broadcast limit for channel scheme", + mlog.String("scheme_id", scheme.Id), + mlog.Int("totalBroadcasts", totalBroadcasts)) + break + } + offset += pageSize + } + case model.SchemeScopePlaybook, model.SchemeScopeRun: + // Playbook/run schemes don't map to teams or channels; broadcast globally. + publishEvent("", "") + default: + return model.NewAppError("sendUpdatedRoleEvent", "app.role.send_updated_role_event.unknown_scope", nil, fmt.Sprintf("unknown scheme scope: %s", scheme.Scope), http.StatusInternalServerError) + } return nil } diff --git a/server/channels/app/role_test.go b/server/channels/app/role_test.go index 460d43d882..bb876fa6bf 100644 --- a/server/channels/app/role_test.go +++ b/server/channels/app/role_test.go @@ -6,6 +6,7 @@ package app import ( "context" "encoding/csv" + "errors" "io" "os" "slices" @@ -13,9 +14,11 @@ import ( "strings" "testing" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks" ) type permissionInheritanceTestData struct { @@ -261,3 +264,225 @@ func testPermissionInheritance(t *testing.T, testCallback func(t *testing.T, th // test 24 combinations where the higher-scoped scheme is a TEAM scheme test(teamScheme.DefaultChannelGuestRole, teamScheme.DefaultChannelUserRole, teamScheme.DefaultChannelAdminRole) } + +func TestSendUpdatedRoleEvent(t *testing.T) { + t.Run("BuiltIn role broadcasts globally without a DB lookup", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockStore.On("Scheme").Return(&mockSchemeStore) + + role := &model.Role{Name: model.TeamAdminRoleId, BuiltIn: true} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockSchemeStore.AssertNotCalled(t, "Get", mock.Anything) + }) + + t.Run("Team scheme role calls GetTeamsByScheme and emits per-team events", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeTeam} + teams := []*model.Team{{Id: model.NewId()}, {Id: model.NewId()}} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockTeamStore := mocks.TeamStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockTeamStore.On("GetTeamsByScheme", schemeID, 0, 1000).Return(teams, nil) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Team").Return(&mockTeamStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockSchemeStore.AssertCalled(t, "Get", schemeID) + mockTeamStore.AssertCalled(t, "GetTeamsByScheme", schemeID, 0, 1000) + }) + + t.Run("Channel scheme role calls GetChannelsByScheme and emits per-channel events", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeChannel} + channels := model.ChannelList{{Id: model.NewId()}} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockChannelStore := mocks.ChannelStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockChannelStore.On("GetChannelsByScheme", schemeID, 0, 1000).Return(channels, nil) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Channel").Return(&mockChannelStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockSchemeStore.AssertCalled(t, "Get", schemeID) + mockChannelStore.AssertCalled(t, "GetChannelsByScheme", schemeID, 0, 1000) + }) + + t.Run("Role not in any scheme broadcasts globally", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockTeamStore := mocks.TeamStore{} + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Team").Return(&mockTeamStore) + + role := &model.Role{Name: model.NewId(), BuiltIn: false, SchemeId: nil} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockSchemeStore.AssertNotCalled(t, "Get", mock.Anything) + mockTeamStore.AssertNotCalled(t, "GetTeamsByScheme", mock.Anything, mock.Anything, mock.Anything) + }) + + t.Run("Playbook scope falls back to global broadcast without querying teams or channels", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopePlaybook} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockTeamStore := mocks.TeamStore{} + mockChannelStore := mocks.ChannelStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Team").Return(&mockTeamStore) + mockStore.On("Channel").Return(&mockChannelStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockTeamStore.AssertNotCalled(t, "GetTeamsByScheme", mock.Anything, mock.Anything, mock.Anything) + mockChannelStore.AssertNotCalled(t, "GetChannelsByScheme", mock.Anything, mock.Anything, mock.Anything) + }) + + t.Run("Scheme store error is logged and skips broadcast", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockSchemeStore.On("Get", schemeID).Return(nil, errors.New("db error")) + mockStore.On("Scheme").Return(&mockSchemeStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + }) + + t.Run("GetTeamsByScheme store error propagates as AppError", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeTeam} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockTeamStore := mocks.TeamStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockTeamStore.On("GetTeamsByScheme", schemeID, 0, 1000).Return(nil, errors.New("db error")) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Team").Return(&mockTeamStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.NotNil(t, appErr) + }) + + t.Run("Team scheme paginates across multiple pages", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeTeam} + + // Build a full first page (1000 teams) and a partial second page (2 teams). + page1 := make([]*model.Team, 1000) + for i := range page1 { + page1[i] = &model.Team{Id: model.NewId()} + } + page2 := []*model.Team{{Id: model.NewId()}, {Id: model.NewId()}} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockTeamStore := mocks.TeamStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockTeamStore.On("GetTeamsByScheme", schemeID, 0, 1000).Return(page1, nil) + mockTeamStore.On("GetTeamsByScheme", schemeID, 1000, 1000).Return(page2, nil) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Team").Return(&mockTeamStore) + + role := &model.Role{Name: model.NewId(), BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockTeamStore.AssertCalled(t, "GetTeamsByScheme", schemeID, 0, 1000) + mockTeamStore.AssertCalled(t, "GetTeamsByScheme", schemeID, 1000, 1000) + }) + + t.Run("Channel scheme paginates across multiple pages", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeChannel} + + page1 := make(model.ChannelList, 1000) + for i := range page1 { + page1[i] = &model.Channel{Id: model.NewId()} + } + page2 := model.ChannelList{{Id: model.NewId()}, {Id: model.NewId()}, {Id: model.NewId()}} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockChannelStore := mocks.ChannelStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockChannelStore.On("GetChannelsByScheme", schemeID, 0, 1000).Return(page1, nil) + mockChannelStore.On("GetChannelsByScheme", schemeID, 1000, 1000).Return(page2, nil) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Channel").Return(&mockChannelStore) + + role := &model.Role{Name: model.NewId(), BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.Nil(t, appErr) + mockChannelStore.AssertCalled(t, "GetChannelsByScheme", schemeID, 0, 1000) + mockChannelStore.AssertCalled(t, "GetChannelsByScheme", schemeID, 1000, 1000) + }) + + t.Run("GetChannelsByScheme store error propagates as AppError", func(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + schemeID := model.NewId() + roleName := model.NewId() + scheme := &model.Scheme{Id: schemeID, Scope: model.SchemeScopeChannel} + + mockStore := th.App.Srv().Store().(*mocks.Store) + mockSchemeStore := mocks.SchemeStore{} + mockChannelStore := mocks.ChannelStore{} + mockSchemeStore.On("Get", schemeID).Return(scheme, nil) + mockChannelStore.On("GetChannelsByScheme", schemeID, 0, 1000).Return(nil, errors.New("db error")) + mockStore.On("Scheme").Return(&mockSchemeStore) + mockStore.On("Channel").Return(&mockChannelStore) + + role := &model.Role{Name: roleName, BuiltIn: false, SchemeId: &schemeID} + appErr := th.App.sendUpdatedRoleEvent(role) + require.NotNil(t, appErr) + }) +} diff --git a/server/channels/db/migrations/migrations.list b/server/channels/db/migrations/migrations.list index 026d392d38..2a85e8eace 100644 --- a/server/channels/db/migrations/migrations.list +++ b/server/channels/db/migrations/migrations.list @@ -279,6 +279,12 @@ channels/db/migrations/mysql/000140_add_lastmemberssyncat_to_sharedchannelremote channels/db/migrations/mysql/000140_add_lastmemberssyncat_to_sharedchannelremotes.up.sql channels/db/migrations/mysql/000141_add_remoteid_channelid_to_post_acknowledgements.down.sql channels/db/migrations/mysql/000141_add_remoteid_channelid_to_post_acknowledgements.up.sql +channels/db/migrations/mysql/000142_add_schemeid_to_roles.down.sql +channels/db/migrations/mysql/000142_add_schemeid_to_roles.up.sql +channels/db/migrations/mysql/000143_backfill_roles_schemeid.down.sql +channels/db/migrations/mysql/000143_backfill_roles_schemeid.up.sql +channels/db/migrations/mysql/000144_add_roles_schemeid_index.down.sql +channels/db/migrations/mysql/000144_add_roles_schemeid_index.up.sql channels/db/migrations/postgres/000001_create_teams.down.sql channels/db/migrations/postgres/000001_create_teams.up.sql channels/db/migrations/postgres/000002_create_team_members.down.sql @@ -559,3 +565,9 @@ channels/db/migrations/postgres/000140_add_lastmemberssyncat_to_sharedchannelrem channels/db/migrations/postgres/000140_add_lastmemberssyncat_to_sharedchannelremotes.up.sql channels/db/migrations/postgres/000141_add_remoteid_channelid_to_post_acknowledgements.down.sql channels/db/migrations/postgres/000141_add_remoteid_channelid_to_post_acknowledgements.up.sql +channels/db/migrations/postgres/000142_add_schemeid_to_roles.down.sql +channels/db/migrations/postgres/000142_add_schemeid_to_roles.up.sql +channels/db/migrations/postgres/000143_backfill_roles_schemeid.down.sql +channels/db/migrations/postgres/000143_backfill_roles_schemeid.up.sql +channels/db/migrations/postgres/000144_add_roles_schemeid_index.down.sql +channels/db/migrations/postgres/000144_add_roles_schemeid_index.up.sql diff --git a/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.down.sql b/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.down.sql new file mode 100644 index 0000000000..a932210d9e --- /dev/null +++ b/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.down.sql @@ -0,0 +1,14 @@ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Roles' + AND table_schema = DATABASE() + AND column_name = 'SchemeId' + ) > 0, + 'ALTER TABLE Roles DROP COLUMN SchemeId;', + 'SELECT 1' +)); + +PREPARE dropColumnIfExists FROM @preparedStatement; +EXECUTE dropColumnIfExists; +DEALLOCATE PREPARE dropColumnIfExists; diff --git a/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.up.sql b/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.up.sql new file mode 100644 index 0000000000..155f9c62f5 --- /dev/null +++ b/server/channels/db/migrations/mysql/000142_add_schemeid_to_roles.up.sql @@ -0,0 +1,14 @@ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Roles' + AND table_schema = DATABASE() + AND column_name = 'SchemeId' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Roles ADD COLUMN SchemeId varchar(26);' +)); + +PREPARE addColumnIfNotExists FROM @preparedStatement; +EXECUTE addColumnIfNotExists; +DEALLOCATE PREPARE addColumnIfNotExists; diff --git a/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.down.sql b/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.down.sql new file mode 100644 index 0000000000..c2e2bbd89f --- /dev/null +++ b/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.down.sql @@ -0,0 +1 @@ +UPDATE Roles SET SchemeId = NULL; diff --git a/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.up.sql b/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.up.sql new file mode 100644 index 0000000000..c431822bb0 --- /dev/null +++ b/server/channels/db/migrations/mysql/000143_backfill_roles_schemeid.up.sql @@ -0,0 +1,19 @@ +UPDATE Roles r +INNER JOIN ( + SELECT role_name, MIN(scheme_id) AS scheme_id + FROM ( + SELECT Id AS scheme_id, DefaultTeamAdminRole AS role_name FROM Schemes + UNION ALL SELECT Id, DefaultTeamUserRole FROM Schemes + UNION ALL SELECT Id, DefaultTeamGuestRole FROM Schemes + UNION ALL SELECT Id, DefaultChannelAdminRole FROM Schemes + UNION ALL SELECT Id, DefaultChannelUserRole FROM Schemes + UNION ALL SELECT Id, DefaultChannelGuestRole FROM Schemes + UNION ALL SELECT Id, DefaultPlaybookAdminRole FROM Schemes + UNION ALL SELECT Id, DefaultPlaybookMemberRole FROM Schemes + UNION ALL SELECT Id, DefaultRunAdminRole FROM Schemes + UNION ALL SELECT Id, DefaultRunMemberRole FROM Schemes + ) expanded + WHERE role_name IS NOT NULL AND role_name <> '' + GROUP BY role_name +) m ON r.Name = m.role_name +SET r.SchemeId = m.scheme_id; diff --git a/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.down.sql b/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.down.sql new file mode 100644 index 0000000000..ec1b704d31 --- /dev/null +++ b/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.down.sql @@ -0,0 +1,14 @@ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Roles' + AND table_schema = DATABASE() + AND index_name = 'idx_roles_scheme_id' + ) > 0, + 'DROP INDEX idx_roles_scheme_id ON Roles;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; diff --git a/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.up.sql b/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.up.sql new file mode 100644 index 0000000000..2b1a037a58 --- /dev/null +++ b/server/channels/db/migrations/mysql/000144_add_roles_schemeid_index.up.sql @@ -0,0 +1,14 @@ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Roles' + AND table_schema = DATABASE() + AND index_name = 'idx_roles_scheme_id' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_roles_scheme_id ON Roles(SchemeId);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; diff --git a/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.down.sql b/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.down.sql new file mode 100644 index 0000000000..ac12dd12e4 --- /dev/null +++ b/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.down.sql @@ -0,0 +1 @@ +ALTER TABLE roles DROP COLUMN IF EXISTS schemeid; diff --git a/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.up.sql b/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.up.sql new file mode 100644 index 0000000000..6c3612417e --- /dev/null +++ b/server/channels/db/migrations/postgres/000142_add_schemeid_to_roles.up.sql @@ -0,0 +1 @@ +ALTER TABLE roles ADD COLUMN IF NOT EXISTS schemeid VARCHAR(26); diff --git a/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.down.sql b/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.down.sql new file mode 100644 index 0000000000..efb09e806d --- /dev/null +++ b/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.down.sql @@ -0,0 +1 @@ +UPDATE roles SET schemeid = NULL; diff --git a/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.up.sql b/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.up.sql new file mode 100644 index 0000000000..1c4acba08b --- /dev/null +++ b/server/channels/db/migrations/postgres/000143_backfill_roles_schemeid.up.sql @@ -0,0 +1,23 @@ +UPDATE roles +SET schemeid = match.scheme_id +FROM ( + SELECT DISTINCT ON (role_name) id AS scheme_id, role_name + FROM ( + SELECT id, unnest(ARRAY[ + defaultteamadminrole, + defaultteamuserrole, + defaultteamguestrole, + defaultchanneladminrole, + defaultchanneluserrole, + defaultchannelguestrole, + defaultplaybookadminrole, + defaultplaybookmemberrole, + defaultrunadminrole, + defaultrunmemberrole + ]) AS role_name + FROM schemes + ) expanded + WHERE role_name IS NOT NULL AND role_name <> '' + ORDER BY role_name, scheme_id +) match +WHERE roles.name = match.role_name; diff --git a/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.down.sql b/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.down.sql new file mode 100644 index 0000000000..61b69a2613 --- /dev/null +++ b/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.down.sql @@ -0,0 +1,2 @@ +-- morph:nontransactional +DROP INDEX CONCURRENTLY IF EXISTS idx_roles_scheme_id; diff --git a/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.up.sql b/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.up.sql new file mode 100644 index 0000000000..16b6d4198a --- /dev/null +++ b/server/channels/db/migrations/postgres/000144_add_roles_schemeid_index.up.sql @@ -0,0 +1,2 @@ +-- morph:nontransactional +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_roles_scheme_id ON roles(schemeid); diff --git a/server/channels/store/sqlstore/role_store.go b/server/channels/store/sqlstore/role_store.go index 544ae27744..377fcb9f0a 100644 --- a/server/channels/store/sqlstore/role_store.go +++ b/server/channels/store/sqlstore/role_store.go @@ -33,6 +33,7 @@ type Role struct { Permissions string SchemeManaged bool BuiltIn bool + SchemeId *string } type channelRolesPermissions struct { @@ -66,6 +67,7 @@ func NewRoleFromModel(role *model.Role) *Role { Permissions: permissions, SchemeManaged: role.SchemeManaged, BuiltIn: role.BuiltIn, + SchemeId: role.SchemeId, } } @@ -81,6 +83,7 @@ func (role Role) ToModel() *model.Role { Permissions: strings.Fields(role.Permissions), SchemeManaged: role.SchemeManaged, BuiltIn: role.BuiltIn, + SchemeId: role.SchemeId, } } @@ -90,7 +93,7 @@ func newSqlRoleStore(sqlStore *SqlStore) store.RoleStore { } s.tableSelectQuery = s.getQueryBuilder(). - Select("Id", "Name", "DisplayName", "Description", "CreateAt", "UpdateAt", "DeleteAt", "Permissions", "SchemeManaged", "BuiltIn"). + Select("Id", "Name", "DisplayName", "Description", "CreateAt", "UpdateAt", "DeleteAt", "Permissions", "SchemeManaged", "BuiltIn", "SchemeId"). From("Roles") return &s @@ -122,9 +125,10 @@ func (s *SqlRoleStore) Save(role *model.Role) (_ *model.Role, err error) { dbRole.UpdateAt = model.GetMillis() res, err := s.GetMaster().NamedExec(`UPDATE Roles - SET UpdateAt=:UpdateAt, DeleteAt=:DeleteAt, CreateAt=:CreateAt, Name=:Name, DisplayName=:DisplayName, - Description=:Description, Permissions=:Permissions, SchemeManaged=:SchemeManaged, BuiltIn=:BuiltIn - WHERE Id=:Id`, &dbRole) + SET UpdateAt=:UpdateAt, DeleteAt=:DeleteAt, CreateAt=:CreateAt, Name=:Name, DisplayName=:DisplayName, + Description=:Description, Permissions=:Permissions, SchemeManaged=:SchemeManaged, BuiltIn=:BuiltIn, + SchemeId=:SchemeId + WHERE Id=:Id`, &dbRole) if err != nil { return nil, errors.Wrap(err, "failed to update Role") @@ -155,9 +159,9 @@ func (s *SqlRoleStore) createRole(role *model.Role, transaction *sqlxTxWrapper) dbRole.UpdateAt = dbRole.CreateAt if _, err := transaction.NamedExec(`INSERT INTO Roles - (Id, Name, DisplayName, Description, Permissions, CreateAt, UpdateAt, DeleteAt, SchemeManaged, BuiltIn) + (Id, Name, DisplayName, Description, Permissions, CreateAt, UpdateAt, DeleteAt, SchemeManaged, BuiltIn, SchemeId) VALUES - (:Id, :Name, :DisplayName, :Description, :Permissions, :CreateAt, :UpdateAt, :DeleteAt, :SchemeManaged, :BuiltIn)`, dbRole); err != nil { + (:Id, :Name, :DisplayName, :Description, :Permissions, :CreateAt, :UpdateAt, :DeleteAt, :SchemeManaged, :BuiltIn, :SchemeId)`, dbRole); err != nil { return nil, errors.Wrap(err, "failed to save Role") } @@ -230,7 +234,7 @@ func (s *SqlRoleStore) GetByNames(names []string) ([]*model.Role, error) { err = rows.Scan( &role.Id, &role.Name, &role.DisplayName, &role.Description, &role.CreateAt, &role.UpdateAt, &role.DeleteAt, &role.Permissions, - &role.SchemeManaged, &role.BuiltIn) + &role.SchemeManaged, &role.BuiltIn, &role.SchemeId) if err != nil { return nil, errors.Wrap(err, "failed to scan values") } @@ -394,9 +398,10 @@ func (s *SqlRoleStore) AllChannelSchemeRoles() ([]*model.Role, error) { "Roles.Permissions", "Roles.SchemeManaged", "Roles.BuiltIn", + "Roles.SchemeId", ). - From("Schemes"). - Join("Roles ON Schemes.DefaultChannelGuestRole = Roles.Name OR Schemes.DefaultChannelUserRole = Roles.Name OR Schemes.DefaultChannelAdminRole = Roles.Name"). + From("Roles"). + Join("Schemes ON Roles.SchemeId = Schemes.Id"). Where(sq.Eq{"Schemes.Scope": model.SchemeScopeChannel}). Where(sq.Eq{"Roles.DeleteAt": 0}). Where(sq.Eq{"Schemes.DeleteAt": 0}) @@ -433,13 +438,14 @@ func (s *SqlRoleStore) ChannelRolesUnderTeamRole(roleName string) ([]*model.Role "ChannelSchemeRoles.Permissions", "ChannelSchemeRoles.SchemeManaged", "ChannelSchemeRoles.BuiltIn", + "ChannelSchemeRoles.SchemeId", ). From("Roles AS HigherScopedRoles"). Join("Schemes AS HigherScopedSchemes ON (HigherScopedRoles.Name = HigherScopedSchemes.DefaultChannelGuestRole OR HigherScopedRoles.Name = HigherScopedSchemes.DefaultChannelUserRole OR HigherScopedRoles.Name = HigherScopedSchemes.DefaultChannelAdminRole)"). Join("Teams ON Teams.SchemeId = HigherScopedSchemes.Id"). Join("Channels ON Channels.TeamId = Teams.Id"). Join("Schemes AS ChannelSchemes ON Channels.SchemeId = ChannelSchemes.Id"). - Join("Roles AS ChannelSchemeRoles ON (ChannelSchemeRoles.Name = ChannelSchemes.DefaultChannelGuestRole OR ChannelSchemeRoles.Name = ChannelSchemes.DefaultChannelUserRole OR ChannelSchemeRoles.Name = ChannelSchemes.DefaultChannelAdminRole)"). + Join("Roles AS ChannelSchemeRoles ON ChannelSchemeRoles.SchemeId = ChannelSchemes.Id"). Where(sq.Eq{"HigherScopedSchemes.Scope": model.SchemeScopeTeam}). Where(sq.Eq{"HigherScopedRoles.Name": roleName}). Where(sq.Eq{"HigherScopedRoles.DeleteAt": 0}). diff --git a/server/channels/store/sqlstore/scheme_store.go b/server/channels/store/sqlstore/scheme_store.go index 182f773901..c42962fd82 100644 --- a/server/channels/store/sqlstore/scheme_store.go +++ b/server/channels/store/sqlstore/scheme_store.go @@ -100,6 +100,12 @@ func (s *SqlSchemeStore) Save(scheme *model.Scheme) (_ *model.Scheme, err error) } func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxWrapper) (*model.Scheme, error) { + // Generate the scheme ID up front so it can be recorded on each created role. + scheme.Id = model.NewId() + if scheme.Name == "" { + scheme.Name = model.NewId() + } + // Fetch the default system scheme roles to populate default permissions. defaultRoleNames := []string{ model.TeamAdminRoleId, @@ -135,6 +141,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNameTeamAdmin, scheme.Name), Permissions: defaultRoles[model.TeamAdminRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err := s.SqlStore.Role().(*SqlRoleStore).createRole(teamAdminRole, transaction) @@ -149,6 +156,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNameTeamUser, scheme.Name), Permissions: defaultRoles[model.TeamUserRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(teamUserRole, transaction) @@ -163,6 +171,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNameTeamGuest, scheme.Name), Permissions: defaultRoles[model.TeamGuestRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(teamGuestRole, transaction) @@ -177,6 +186,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNamePlaybookAdmin, scheme.Name), Permissions: defaultRoles[model.PlaybookAdminRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(playbookAdminRole, transaction) if err != nil { @@ -190,6 +200,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNamePlaybookMember, scheme.Name), Permissions: defaultRoles[model.PlaybookMemberRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(playbookMemberRole, transaction) if err != nil { @@ -203,6 +214,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNameRunAdmin, scheme.Name), Permissions: defaultRoles[model.RunAdminRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(runAdminRole, transaction) if err != nil { @@ -216,6 +228,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("%s %s", SchemeRoleDisplayNameRunMember, scheme.Name), Permissions: defaultRoles[model.RunMemberRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(runMemberRole, transaction) if err != nil { @@ -231,6 +244,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("Channel Admin Role for Scheme %s", scheme.Name), Permissions: defaultRoles[model.ChannelAdminRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } if scheme.Scope == model.SchemeScopeChannel { @@ -249,6 +263,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("Channel User Role for Scheme %s", scheme.Name), Permissions: defaultRoles[model.ChannelUserRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } if scheme.Scope == model.SchemeScopeChannel { @@ -267,6 +282,7 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW DisplayName: fmt.Sprintf("Channel Guest Role for Scheme %s", scheme.Name), Permissions: defaultRoles[model.ChannelGuestRoleId].Permissions, SchemeManaged: true, + SchemeId: &scheme.Id, } if scheme.Scope == model.SchemeScopeChannel { @@ -280,10 +296,6 @@ func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *sqlxTxW scheme.DefaultChannelGuestRole = savedRole.Name } - scheme.Id = model.NewId() - if scheme.Name == "" { - scheme.Name = model.NewId() - } scheme.CreateAt = model.GetMillis() scheme.UpdateAt = scheme.CreateAt @@ -363,23 +375,11 @@ func (s *SqlSchemeStore) Delete(schemeId string) (*model.Scheme, error) { s.Channel().ClearCaches() // Delete the roles belonging to the scheme. - roleNames := []string{scheme.DefaultChannelGuestRole, scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole} - if scheme.Scope == model.SchemeScopeTeam { - roleNames = append(roleNames, scheme.DefaultTeamGuestRole, scheme.DefaultTeamUserRole, scheme.DefaultTeamAdminRole) - } - if scheme.Scope == model.SchemeScopePlaybook { - roleNames = append(roleNames, scheme.DefaultPlaybookAdminRole, scheme.DefaultPlaybookMemberRole) - } - - if scheme.Scope == model.SchemeScopeRun { - roleNames = append(roleNames, scheme.DefaultRunAdminRole, scheme.DefaultRunMemberRole) - } - time := model.GetMillis() updateQuery, args, err := s.getQueryBuilder(). Update("Roles"). - Where(sq.Eq{"Name": roleNames}). + Where(sq.Eq{"SchemeId": schemeId}). Set("UpdateAt", time). Set("DeleteAt", time). ToSql() @@ -388,7 +388,7 @@ func (s *SqlSchemeStore) Delete(schemeId string) (*model.Scheme, error) { } if _, err = s.GetMaster().Exec(updateQuery, args...); err != nil { - return nil, errors.Wrapf(err, "failed to update Roles with name in (%s)", roleNames) + return nil, errors.Wrapf(err, "failed to update Roles with SchemeId=%s", schemeId) } // Delete the scheme itself. diff --git a/server/channels/store/storetest/role_store.go b/server/channels/store/storetest/role_store.go index d7dcb8bab7..2e33d98b90 100644 --- a/server/channels/store/storetest/role_store.go +++ b/server/channels/store/storetest/role_store.go @@ -456,39 +456,45 @@ func testRoleStoreLowerScopedChannelSchemeRoles(t *testing.T, rctx request.CTX, actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelGuestRole) require.NoError(t, err) - var actualRoleNames []string + roleByName := make(map[string]*model.Role, len(actualRoles)) for _, role := range actualRoles { - actualRoleNames = append(actualRoleNames, role.Name) + roleByName[role.Name] = role } - require.Contains(t, actualRoleNames, channelScheme1.DefaultChannelGuestRole) - require.NotContains(t, actualRoleNames, channelScheme2.DefaultChannelGuestRole) + require.Contains(t, roleByName, channelScheme1.DefaultChannelGuestRole) + require.NotContains(t, roleByName, channelScheme2.DefaultChannelGuestRole) + require.NotNil(t, roleByName[channelScheme1.DefaultChannelGuestRole].SchemeId) + assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelGuestRole].SchemeId) }) t.Run("user role for the right team's channels are returned", func(t *testing.T) { actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelUserRole) require.NoError(t, err) - var actualRoleNames []string + roleByName := make(map[string]*model.Role, len(actualRoles)) for _, role := range actualRoles { - actualRoleNames = append(actualRoleNames, role.Name) + roleByName[role.Name] = role } - require.Contains(t, actualRoleNames, channelScheme1.DefaultChannelUserRole) - require.NotContains(t, actualRoleNames, channelScheme2.DefaultChannelUserRole) + require.Contains(t, roleByName, channelScheme1.DefaultChannelUserRole) + require.NotContains(t, roleByName, channelScheme2.DefaultChannelUserRole) + require.NotNil(t, roleByName[channelScheme1.DefaultChannelUserRole].SchemeId) + assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelUserRole].SchemeId) }) t.Run("admin role for the right team's channels are returned", func(t *testing.T) { actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelAdminRole) require.NoError(t, err) - var actualRoleNames []string + roleByName := make(map[string]*model.Role, len(actualRoles)) for _, role := range actualRoles { - actualRoleNames = append(actualRoleNames, role.Name) + roleByName[role.Name] = role } - require.Contains(t, actualRoleNames, channelScheme1.DefaultChannelAdminRole) - require.NotContains(t, actualRoleNames, channelScheme2.DefaultChannelAdminRole) + require.Contains(t, roleByName, channelScheme1.DefaultChannelAdminRole) + require.NotContains(t, roleByName, channelScheme2.DefaultChannelAdminRole) + require.NotNil(t, roleByName[channelScheme1.DefaultChannelAdminRole].SchemeId) + assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelAdminRole].SchemeId) }) }) @@ -497,9 +503,9 @@ func testRoleStoreLowerScopedChannelSchemeRoles(t *testing.T, rctx request.CTX, actualRoles, err := ss.Role().AllChannelSchemeRoles() require.NoError(t, err) - var actualRoleNames []string + roleByName := make(map[string]*model.Role, len(actualRoles)) for _, role := range actualRoles { - actualRoleNames = append(actualRoleNames, role.Name) + roleByName[role.Name] = role } allRoleNames := []string{ @@ -514,7 +520,27 @@ func testRoleStoreLowerScopedChannelSchemeRoles(t *testing.T, rctx request.CTX, } for _, roleName := range allRoleNames { - require.Contains(t, actualRoleNames, roleName) + require.Contains(t, roleByName, roleName) + } + + // Roles for channelScheme1 must carry channelScheme1's ID. + for _, roleName := range []string{ + channelScheme1.DefaultChannelGuestRole, + channelScheme1.DefaultChannelUserRole, + channelScheme1.DefaultChannelAdminRole, + } { + require.NotNil(t, roleByName[roleName].SchemeId) + assert.Equal(t, channelScheme1.Id, *roleByName[roleName].SchemeId) + } + + // Roles for channelScheme2 must carry channelScheme2's ID. + for _, roleName := range []string{ + channelScheme2.DefaultChannelGuestRole, + channelScheme2.DefaultChannelUserRole, + channelScheme2.DefaultChannelAdminRole, + } { + require.NotNil(t, roleByName[roleName].SchemeId) + assert.Equal(t, channelScheme2.Id, *roleByName[roleName].SchemeId) } }) }) diff --git a/server/channels/store/storetest/scheme_store.go b/server/channels/store/storetest/scheme_store.go index 386fb86dca..5e8f0962fc 100644 --- a/server/channels/store/storetest/scheme_store.go +++ b/server/channels/store/storetest/scheme_store.go @@ -174,6 +174,28 @@ func testSchemeStoreSave(t *testing.T, rctx request.CTX, ss store.Store) { assert.Equal(t, role6.Permissions, []string{"read_channel", "read_channel_content", "create_post"}) assert.True(t, role6.SchemeManaged) + role7, err := ss.Role().GetByName(context.Background(), d1.DefaultPlaybookAdminRole) + assert.NoError(t, err) + assert.True(t, role7.SchemeManaged) + + role8, err := ss.Role().GetByName(context.Background(), d1.DefaultPlaybookMemberRole) + assert.NoError(t, err) + assert.True(t, role8.SchemeManaged) + + role9, err := ss.Role().GetByName(context.Background(), d1.DefaultRunAdminRole) + assert.NoError(t, err) + assert.True(t, role9.SchemeManaged) + + role10, err := ss.Role().GetByName(context.Background(), d1.DefaultRunMemberRole) + assert.NoError(t, err) + assert.True(t, role10.SchemeManaged) + + // Every role created for a scheme must carry the scheme's ID. + for _, role := range []*model.Role{role1, role2, role3, role4, role5, role6, role7, role8, role9, role10} { + require.NotNil(t, role.SchemeId) + assert.Equal(t, d1.Id, *role.SchemeId) + } + // Change the scheme description and update. d1.Description = model.NewId() diff --git a/server/channels/testlib/testdata/mysql_migration_warmup.sql b/server/channels/testlib/testdata/mysql_migration_warmup.sql index eaafb2d368..c52bc419de 100644 --- a/server/channels/testlib/testdata/mysql_migration_warmup.sql +++ b/server/channels/testlib/testdata/mysql_migration_warmup.sql @@ -68,29 +68,29 @@ UNLOCK TABLES; LOCK TABLES `Roles` WRITE; /*!40000 ALTER TABLE `Roles` DISABLE KEYS */; -INSERT INTO `Roles` VALUES ('3ndsqn4sbbyjxpzccrzmzejstw','team_guest','authentication.roles.team_guest.name','authentication.roles.team_guest.description',1605167829008,1662271986894,0,' view_team',1,1); -INSERT INTO `Roles` VALUES ('44bq9f9s93b7f811ex5r1b4s1w','system_custom_group_admin','authentication.roles.system_custom_group_admin.name','authentication.roles.system_custom_group_admin.description',1662271985879,1662271986897,0,' manage_custom_group_members create_custom_group edit_custom_group delete_custom_group',0,1); -INSERT INTO `Roles` VALUES ('6jaz4y4nmjnxunkmogjf95fiha','system_user_manager','authentication.roles.system_user_manager.name','authentication.roles.system_user_manager.description',0,1662271986902,0,' delete_public_channel sysconsole_write_user_management_channels list_public_teams sysconsole_read_authentication_ldap sysconsole_write_user_management_groups manage_private_channel_members sysconsole_read_user_management_permissions sysconsole_read_authentication_password read_channel join_private_teams manage_team sysconsole_read_user_management_teams sysconsole_read_authentication_email sysconsole_write_user_management_teams read_public_channel_groups list_private_teams convert_private_channel_to_public manage_team_roles convert_public_channel_to_private sysconsole_read_authentication_openid view_team add_user_to_team read_ldap_sync_job read_public_channel test_ldap manage_private_channel_properties delete_private_channel manage_channel_roles sysconsole_read_authentication_guest_access sysconsole_read_user_management_channels sysconsole_read_user_management_groups manage_public_channel_members sysconsole_read_authentication_saml remove_user_from_team join_public_teams manage_public_channel_properties sysconsole_read_authentication_mfa sysconsole_read_authentication_signup read_private_channel_groups',0,1); -INSERT INTO `Roles` VALUES ('6pahsh5hg7rpjfhz4f5c1wsbfw','team_admin','authentication.roles.team_admin.name','authentication.roles.team_admin.description',0,1662271986906,0,' remove_user_from_team manage_slash_commands manage_team_roles delete_others_posts manage_others_slash_commands import_team manage_others_outgoing_webhooks convert_private_channel_to_public delete_post playbook_private_manage_roles convert_public_channel_to_private manage_channel_roles playbook_public_manage_roles manage_outgoing_webhooks manage_others_incoming_webhooks manage_incoming_webhooks manage_team',1,1); -INSERT INTO `Roles` VALUES ('c7oo8yeiojfu8xjyuyxn3fhxpc','team_post_all','authentication.roles.team_post_all.name','authentication.roles.team_post_all.description',0,1662271986910,0,' create_post use_channel_mentions',0,1); -INSERT INTO `Roles` VALUES ('cmqctq1egt877y9ua9pdsknoiw','team_post_all_public','authentication.roles.team_post_all_public.name','authentication.roles.team_post_all_public.description',0,1662271986914,0,' create_post_public use_channel_mentions',0,1); -INSERT INTO `Roles` VALUES ('dmmkxxmi3b8pdgcq9pjtf6mfao','playbook_member','authentication.roles.playbook_member.name','authentication.roles.playbook_member.description',1662271985811,1662271986918,0,' playbook_private_manage_members playbook_private_manage_properties run_create playbook_public_view playbook_public_manage_members playbook_public_manage_properties playbook_private_view',1,1); -INSERT INTO `Roles` VALUES ('dwjkqj9bj7r4xr8zb18z3tg53y','playbook_admin','authentication.roles.playbook_admin.name','authentication.roles.playbook_admin.description',1662271985841,1662271986921,0,' playbook_private_manage_roles playbook_private_manage_properties playbook_public_make_private playbook_public_manage_members playbook_public_manage_roles playbook_public_manage_properties playbook_private_manage_members',1,1); -INSERT INTO `Roles` VALUES ('hh56iy3patffuc3h76soondcga','channel_admin','authentication.roles.channel_admin.name','authentication.roles.channel_admin.description',0,1662271986924,0,' manage_channel_roles use_group_mentions',1,1); -INSERT INTO `Roles` VALUES ('hkcrew7wttb5fbuw3ime6g7nzc','system_read_only_admin','authentication.roles.system_read_only_admin.name','authentication.roles.system_read_only_admin.description',0,1662271986928,0,' sysconsole_read_environment_database sysconsole_read_experimental_features sysconsole_read_compliance_compliance_export sysconsole_read_environment_performance_monitoring sysconsole_read_environment_file_storage sysconsole_read_user_management_channels read_public_channel_groups read_elasticsearch_post_aggregation_job sysconsole_read_integrations_integration_management sysconsole_read_environment_push_notification_server read_compliance_export_job sysconsole_read_user_management_teams sysconsole_read_environment_logging sysconsole_read_about_edition_and_license sysconsole_read_site_customization sysconsole_read_reporting_site_statistics sysconsole_read_site_emoji sysconsole_read_authentication_guest_access test_ldap read_audits sysconsole_read_site_posts download_compliance_export_result sysconsole_read_compliance_compliance_monitoring sysconsole_read_site_announcement_banner sysconsole_read_integrations_gif sysconsole_read_authentication_email sysconsole_read_site_file_sharing_and_downloads sysconsole_read_experimental_bleve sysconsole_read_compliance_data_retention_policy read_channel sysconsole_read_experimental_feature_flags sysconsole_read_environment_image_proxy view_team sysconsole_read_authentication_openid sysconsole_read_environment_web_server sysconsole_read_integrations_cors read_ldap_sync_job sysconsole_read_authentication_saml get_analytics read_private_channel_groups sysconsole_read_reporting_team_statistics sysconsole_read_compliance_custom_terms_of_service sysconsole_read_authentication_ldap sysconsole_read_environment_smtp read_other_users_teams sysconsole_read_user_management_permissions sysconsole_read_environment_session_lengths read_public_channel read_data_retention_job sysconsole_read_user_management_groups sysconsole_read_environment_high_availability sysconsole_read_site_public_links sysconsole_read_authentication_password sysconsole_read_environment_rate_limiting list_public_teams sysconsole_read_site_users_and_teams sysconsole_read_authentication_signup get_logs read_license_information sysconsole_read_site_notices list_private_teams read_elasticsearch_post_indexing_job sysconsole_read_site_notifications sysconsole_read_authentication_mfa sysconsole_read_integrations_bot_accounts sysconsole_read_reporting_server_logs sysconsole_read_site_localization sysconsole_read_environment_elasticsearch sysconsole_read_user_management_users sysconsole_read_plugins sysconsole_read_environment_developer',0,1); -INSERT INTO `Roles` VALUES ('iiwt9pt6wiyb9e1enixtxs5yme','run_admin','authentication.roles.run_admin.name','authentication.roles.run_admin.description',1662271985864,1662271986932,0,' run_manage_properties run_manage_members',1,1); -INSERT INTO `Roles` VALUES ('jg1f1xfh3bb73pua938orwg9ie','system_guest','authentication.roles.global_guest.name','authentication.roles.global_guest.description',1605167829015,1662271986937,0,' create_direct_channel create_group_channel',1,1); -INSERT INTO `Roles` VALUES ('k891n5tpd3n9peue79azejjocy','system_post_all_public','authentication.roles.system_post_all_public.name','authentication.roles.system_post_all_public.description',0,1662271986941,0,' use_channel_mentions create_post_public',0,1); -INSERT INTO `Roles` VALUES ('kb6r9i58x7dxdb3srfohd66sse','system_admin','authentication.roles.global_admin.name','authentication.roles.global_admin.description',0,1662271986948,0,' list_public_teams edit_brand manage_private_channel_properties sysconsole_read_user_management_teams playbook_public_create manage_others_bots invalidate_caches manage_shared_channels sysconsole_write_environment_logging manage_others_outgoing_webhooks sysconsole_read_reporting_team_statistics sysconsole_read_plugins list_team_channels use_group_mentions sysconsole_read_site_users_and_teams sysconsole_write_site_localization get_analytics sysconsole_read_experimental_bleve manage_team_roles sysconsole_read_site_localization edit_post sysconsole_write_user_management_channels test_elasticsearch list_private_teams add_ldap_public_cert join_public_teams manage_slash_commands manage_others_incoming_webhooks manage_public_channel_members sysconsole_read_environment_elasticsearch sysconsole_write_site_customization delete_others_emojis run_manage_members create_emojis sysconsole_write_authentication_email sysconsole_write_compliance_compliance_export add_saml_private_cert create_bot sysconsole_write_environment_rate_limiting add_saml_public_cert edit_other_users sysconsole_write_integrations_integration_management read_user_access_token create_elasticsearch_post_indexing_job sysconsole_write_user_management_users assign_system_admin_role sysconsole_write_user_management_groups sysconsole_read_authentication_guest_access sysconsole_write_about_edition_and_license sysconsole_read_authentication_ldap sysconsole_read_experimental_feature_flags sysconsole_read_integrations_cors sysconsole_read_user_management_groups join_public_channels sysconsole_read_experimental_features test_ldap sysconsole_write_environment_elasticsearch sysconsole_write_reporting_server_logs sysconsole_read_environment_image_proxy sysconsole_read_site_announcement_banner sysconsole_read_reporting_site_statistics sysconsole_write_authentication_mfa sysconsole_read_authentication_openid purge_bleve_indexes playbook_public_manage_members delete_emojis sysconsole_write_environment_file_storage sysconsole_write_reporting_site_statistics playbook_private_manage_members import_team sysconsole_write_environment_web_server sysconsole_write_authentication_password read_public_channel_groups create_compliance_export_job sysconsole_read_authentication_password list_users_without_team sysconsole_read_authentication_mfa add_ldap_private_cert create_data_retention_job read_license_information sysconsole_write_authentication_signup sysconsole_read_environment_push_notification_server edit_others_posts download_compliance_export_result create_ldap_sync_job sysconsole_write_authentication_ldap sysconsole_write_plugins read_data_retention_job sysconsole_write_compliance_data_retention_policy sysconsole_read_site_public_links manage_bots manage_system sysconsole_write_compliance_custom_terms_of_service playbook_public_manage_roles playbook_public_manage_properties playbook_private_create sysconsole_write_experimental_bleve sysconsole_read_authentication_email promote_guest get_saml_cert_status add_user_to_team sysconsole_write_site_users_and_teams create_custom_group manage_private_channel_members read_jobs sysconsole_write_experimental_features read_other_users_teams sysconsole_write_reporting_team_statistics sysconsole_read_environment_file_storage create_post_bleve_indexes_job sysconsole_read_site_file_sharing_and_downloads playbook_private_make_public playbook_public_view create_user_access_token create_public_channel read_channel sysconsole_read_user_management_channels sysconsole_read_user_management_permissions read_public_channel sysconsole_read_compliance_custom_terms_of_service sysconsole_write_site_emoji sysconsole_read_integrations_gif sysconsole_read_site_customization sysconsole_write_integrations_cors invite_user create_direct_channel sysconsole_write_user_management_teams run_create manage_custom_group_members read_ldap_sync_job sysconsole_read_site_notifications playbook_private_manage_properties sysconsole_read_integrations_bot_accounts convert_public_channel_to_private invalidate_email_invite reload_config get_saml_metadata_from_idp manage_secure_connections delete_private_channel sysconsole_read_about_edition_and_license convert_private_channel_to_public sysconsole_read_environment_developer recycle_database_connections remove_saml_private_cert manage_oauth sysconsole_write_environment_database sysconsole_write_site_notifications sysconsole_write_authentication_guest_access sysconsole_write_compliance_compliance_monitoring sysconsole_write_environment_image_proxy create_post_public manage_jobs remove_user_from_team delete_others_posts create_post_ephemeral playbook_private_view create_elasticsearch_post_aggregation_job remove_reaction add_reaction sysconsole_write_environment_high_availability sysconsole_write_authentication_openid sysconsole_write_user_management_permissions add_saml_idp_cert sysconsole_read_site_posts view_members sysconsole_write_environment_smtp sysconsole_read_authentication_saml create_post use_channel_mentions create_team playbook_private_manage_roles get_public_link sysconsole_write_billing manage_system_wide_oauth sysconsole_read_environment_database sysconsole_write_environment_session_lengths run_manage_properties sysconsole_write_authentication_saml sysconsole_read_environment_web_server sysconsole_read_environment_rate_limiting manage_public_channel_properties create_group_channel sysconsole_read_compliance_data_retention_policy sysconsole_read_environment_high_availability manage_others_slash_commands sysconsole_read_compliance_compliance_export delete_custom_group sysconsole_read_user_management_system_roles purge_elasticsearch_indexes view_team sysconsole_read_environment_performance_monitoring manage_channel_roles playbook_public_make_private remove_saml_public_cert demote_to_guest sysconsole_write_environment_performance_monitoring read_audits sysconsole_write_site_announcement_banner upload_file revoke_user_access_token read_others_bots test_email read_elasticsearch_post_aggregation_job sysconsole_read_compliance_compliance_monitoring join_private_teams delete_post sysconsole_write_site_public_links manage_team edit_custom_group sysconsole_write_experimental_feature_flags sysconsole_write_user_management_system_roles remove_others_reactions manage_license_information sysconsole_read_authentication_signup read_compliance_export_job sysconsole_write_environment_developer remove_saml_idp_cert manage_incoming_webhooks sysconsole_read_site_emoji assign_bot sysconsole_write_integrations_gif sysconsole_read_user_management_users delete_public_channel manage_outgoing_webhooks sysconsole_write_site_posts remove_ldap_private_cert sysconsole_write_site_file_sharing_and_downloads sysconsole_read_integrations_integration_management sysconsole_read_environment_logging test_site_url sysconsole_read_environment_session_lengths read_elasticsearch_post_indexing_job sysconsole_read_billing sysconsole_read_site_notices sysconsole_read_reporting_server_logs sysconsole_write_integrations_bot_accounts sysconsole_write_site_notices create_private_channel read_private_channel_groups run_view read_bots manage_roles test_s3 sysconsole_write_environment_push_notification_server get_logs invite_guest remove_ldap_public_cert sysconsole_read_environment_smtp',1,1); -INSERT INTO `Roles` VALUES ('km7kijhdtjbajquwu36uqneyoc','system_post_all','authentication.roles.system_post_all.name','authentication.roles.system_post_all.description',0,1662271986953,0,' create_post use_channel_mentions',0,1); -INSERT INTO `Roles` VALUES ('no7s4436sjbzzqjpupg85mszty','custom_group_user','authentication.roles.custom_group_user.name','authentication.roles.custom_group_user.description',1662271985801,1662271986956,0,'',0,0); -INSERT INTO `Roles` VALUES ('qo7e17c1m3rezyjqx5iq9dpmxe','system_manager','authentication.roles.system_manager.name','authentication.roles.system_manager.description',0,1662271986960,0,' sysconsole_write_environment_image_proxy sysconsole_read_environment_developer read_ldap_sync_job sysconsole_read_reporting_team_statistics recycle_database_connections get_logs read_private_channel_groups test_elasticsearch sysconsole_read_environment_logging purge_elasticsearch_indexes sysconsole_write_site_posts sysconsole_read_environment_database sysconsole_read_environment_performance_monitoring manage_team sysconsole_read_authentication_password sysconsole_write_site_users_and_teams sysconsole_read_user_management_channels sysconsole_write_environment_rate_limiting sysconsole_write_site_notifications read_license_information edit_brand sysconsole_read_plugins sysconsole_read_environment_high_availability sysconsole_read_environment_file_storage sysconsole_read_environment_elasticsearch sysconsole_write_environment_web_server sysconsole_write_environment_smtp sysconsole_write_environment_performance_monitoring sysconsole_write_environment_session_lengths sysconsole_write_user_management_groups convert_private_channel_to_public manage_private_channel_properties sysconsole_read_site_posts list_private_teams sysconsole_read_authentication_ldap sysconsole_read_authentication_guest_access sysconsole_read_site_emoji sysconsole_write_integrations_integration_management convert_public_channel_to_private manage_private_channel_members read_elasticsearch_post_aggregation_job manage_team_roles sysconsole_write_site_file_sharing_and_downloads read_channel read_public_channel sysconsole_read_authentication_openid add_user_to_team sysconsole_write_environment_developer sysconsole_write_site_localization sysconsole_read_about_edition_and_license test_s3 reload_config sysconsole_write_environment_elasticsearch test_site_url sysconsole_write_site_announcement_banner get_analytics sysconsole_read_environment_push_notification_server sysconsole_read_authentication_signup test_email sysconsole_write_integrations_bot_accounts sysconsole_write_integrations_cors view_team sysconsole_write_integrations_gif sysconsole_read_site_notices sysconsole_read_environment_image_proxy sysconsole_read_integrations_cors sysconsole_write_environment_push_notification_server join_public_teams test_ldap create_elasticsearch_post_aggregation_job sysconsole_read_environment_session_lengths sysconsole_write_environment_file_storage manage_public_channel_members sysconsole_write_site_customization sysconsole_read_site_announcement_banner sysconsole_read_environment_smtp sysconsole_write_user_management_teams delete_public_channel sysconsole_write_environment_logging read_public_channel_groups sysconsole_read_site_users_and_teams sysconsole_read_reporting_site_statistics sysconsole_read_site_localization sysconsole_read_site_customization sysconsole_read_environment_rate_limiting sysconsole_read_environment_web_server sysconsole_write_user_management_permissions sysconsole_read_site_file_sharing_and_downloads sysconsole_write_site_public_links sysconsole_read_site_public_links sysconsole_read_authentication_email read_elasticsearch_post_indexing_job sysconsole_read_authentication_saml remove_user_from_team delete_private_channel sysconsole_write_user_management_channels sysconsole_read_reporting_server_logs sysconsole_read_integrations_bot_accounts sysconsole_read_user_management_teams list_public_teams create_elasticsearch_post_indexing_job sysconsole_write_site_emoji invalidate_caches sysconsole_read_integrations_integration_management sysconsole_write_environment_high_availability sysconsole_read_user_management_permissions join_private_teams manage_channel_roles sysconsole_write_site_notices manage_public_channel_properties sysconsole_write_environment_database sysconsole_read_site_notifications sysconsole_read_user_management_groups sysconsole_read_integrations_gif sysconsole_read_authentication_mfa',0,1); -INSERT INTO `Roles` VALUES ('rkr97ikkh7fixy86qsoo5rqm4c','system_user_access_token','authentication.roles.system_user_access_token.name','authentication.roles.system_user_access_token.description',0,1662271986965,0,' create_user_access_token read_user_access_token revoke_user_access_token',0,1); -INSERT INTO `Roles` VALUES ('rxzdk5irm7rcffcfej9e33kqeo','team_user','authentication.roles.team_user.name','authentication.roles.team_user.description',0,1662271986968,0,' invite_user view_team read_public_channel playbook_public_create add_user_to_team playbook_private_create create_private_channel list_team_channels create_public_channel join_public_channels',1,1); -INSERT INTO `Roles` VALUES ('x768jnyzw3rkfx7xb66ehcac6o','channel_user','authentication.roles.channel_user.name','authentication.roles.channel_user.description',0,1662271986972,0,' manage_public_channel_properties create_post manage_private_channel_properties delete_public_channel manage_private_channel_members get_public_link delete_post delete_private_channel upload_file edit_post remove_reaction use_channel_mentions add_reaction read_channel manage_public_channel_members',1,1); -INSERT INTO `Roles` VALUES ('ynn8aynsn7n1trtbuq6p4cyzhe','channel_guest','authentication.roles.channel_guest.name','authentication.roles.channel_guest.description',1605167829001,1662271986975,0,' read_channel add_reaction remove_reaction upload_file edit_post create_post use_channel_mentions',1,1); -INSERT INTO `Roles` VALUES ('yqyby79r9jggxg7a9dnenuawmo','run_member','authentication.roles.run_member.name','authentication.roles.run_member.description',1662271985813,1662271986979,0,' run_view',1,1); -INSERT INTO `Roles` VALUES ('zzehkfnp67bg5g1owh6eptdcxc','system_user','authentication.roles.global_user.name','authentication.roles.global_user.description',0,1662271986983,0,' create_emojis join_public_teams list_public_teams edit_custom_group delete_emojis create_team create_group_channel manage_custom_group_members view_members delete_custom_group create_custom_group create_direct_channel',1,1); +INSERT INTO `Roles` VALUES ('3ndsqn4sbbyjxpzccrzmzejstw','team_guest','authentication.roles.team_guest.name','authentication.roles.team_guest.description',1605167829008,1662271986894,0,' view_team',1,1,NULL); +INSERT INTO `Roles` VALUES ('44bq9f9s93b7f811ex5r1b4s1w','system_custom_group_admin','authentication.roles.system_custom_group_admin.name','authentication.roles.system_custom_group_admin.description',1662271985879,1662271986897,0,' manage_custom_group_members create_custom_group edit_custom_group delete_custom_group',0,1,NULL); +INSERT INTO `Roles` VALUES ('6jaz4y4nmjnxunkmogjf95fiha','system_user_manager','authentication.roles.system_user_manager.name','authentication.roles.system_user_manager.description',0,1662271986902,0,' delete_public_channel sysconsole_write_user_management_channels list_public_teams sysconsole_read_authentication_ldap sysconsole_write_user_management_groups manage_private_channel_members sysconsole_read_user_management_permissions sysconsole_read_authentication_password read_channel join_private_teams manage_team sysconsole_read_user_management_teams sysconsole_read_authentication_email sysconsole_write_user_management_teams read_public_channel_groups list_private_teams convert_private_channel_to_public manage_team_roles convert_public_channel_to_private sysconsole_read_authentication_openid view_team add_user_to_team read_ldap_sync_job read_public_channel test_ldap manage_private_channel_properties delete_private_channel manage_channel_roles sysconsole_read_authentication_guest_access sysconsole_read_user_management_channels sysconsole_read_user_management_groups manage_public_channel_members sysconsole_read_authentication_saml remove_user_from_team join_public_teams manage_public_channel_properties sysconsole_read_authentication_mfa sysconsole_read_authentication_signup read_private_channel_groups',0,1,NULL); +INSERT INTO `Roles` VALUES ('6pahsh5hg7rpjfhz4f5c1wsbfw','team_admin','authentication.roles.team_admin.name','authentication.roles.team_admin.description',0,1662271986906,0,' remove_user_from_team manage_slash_commands manage_team_roles delete_others_posts manage_others_slash_commands import_team manage_others_outgoing_webhooks convert_private_channel_to_public delete_post playbook_private_manage_roles convert_public_channel_to_private manage_channel_roles playbook_public_manage_roles manage_outgoing_webhooks manage_others_incoming_webhooks manage_incoming_webhooks manage_team',1,1,NULL); +INSERT INTO `Roles` VALUES ('c7oo8yeiojfu8xjyuyxn3fhxpc','team_post_all','authentication.roles.team_post_all.name','authentication.roles.team_post_all.description',0,1662271986910,0,' create_post use_channel_mentions',0,1,NULL); +INSERT INTO `Roles` VALUES ('cmqctq1egt877y9ua9pdsknoiw','team_post_all_public','authentication.roles.team_post_all_public.name','authentication.roles.team_post_all_public.description',0,1662271986914,0,' create_post_public use_channel_mentions',0,1,NULL); +INSERT INTO `Roles` VALUES ('dmmkxxmi3b8pdgcq9pjtf6mfao','playbook_member','authentication.roles.playbook_member.name','authentication.roles.playbook_member.description',1662271985811,1662271986918,0,' playbook_private_manage_members playbook_private_manage_properties run_create playbook_public_view playbook_public_manage_members playbook_public_manage_properties playbook_private_view',1,1,NULL); +INSERT INTO `Roles` VALUES ('dwjkqj9bj7r4xr8zb18z3tg53y','playbook_admin','authentication.roles.playbook_admin.name','authentication.roles.playbook_admin.description',1662271985841,1662271986921,0,' playbook_private_manage_roles playbook_private_manage_properties playbook_public_make_private playbook_public_manage_members playbook_public_manage_roles playbook_public_manage_properties playbook_private_manage_members',1,1,NULL); +INSERT INTO `Roles` VALUES ('hh56iy3patffuc3h76soondcga','channel_admin','authentication.roles.channel_admin.name','authentication.roles.channel_admin.description',0,1662271986924,0,' manage_channel_roles use_group_mentions',1,1,NULL); +INSERT INTO `Roles` VALUES ('hkcrew7wttb5fbuw3ime6g7nzc','system_read_only_admin','authentication.roles.system_read_only_admin.name','authentication.roles.system_read_only_admin.description',0,1662271986928,0,' sysconsole_read_environment_database sysconsole_read_experimental_features sysconsole_read_compliance_compliance_export sysconsole_read_environment_performance_monitoring sysconsole_read_environment_file_storage sysconsole_read_user_management_channels read_public_channel_groups read_elasticsearch_post_aggregation_job sysconsole_read_integrations_integration_management sysconsole_read_environment_push_notification_server read_compliance_export_job sysconsole_read_user_management_teams sysconsole_read_environment_logging sysconsole_read_about_edition_and_license sysconsole_read_site_customization sysconsole_read_reporting_site_statistics sysconsole_read_site_emoji sysconsole_read_authentication_guest_access test_ldap read_audits sysconsole_read_site_posts download_compliance_export_result sysconsole_read_compliance_compliance_monitoring sysconsole_read_site_announcement_banner sysconsole_read_integrations_gif sysconsole_read_authentication_email sysconsole_read_site_file_sharing_and_downloads sysconsole_read_experimental_bleve sysconsole_read_compliance_data_retention_policy read_channel sysconsole_read_experimental_feature_flags sysconsole_read_environment_image_proxy view_team sysconsole_read_authentication_openid sysconsole_read_environment_web_server sysconsole_read_integrations_cors read_ldap_sync_job sysconsole_read_authentication_saml get_analytics read_private_channel_groups sysconsole_read_reporting_team_statistics sysconsole_read_compliance_custom_terms_of_service sysconsole_read_authentication_ldap sysconsole_read_environment_smtp read_other_users_teams sysconsole_read_user_management_permissions sysconsole_read_environment_session_lengths read_public_channel read_data_retention_job sysconsole_read_user_management_groups sysconsole_read_environment_high_availability sysconsole_read_site_public_links sysconsole_read_authentication_password sysconsole_read_environment_rate_limiting list_public_teams sysconsole_read_site_users_and_teams sysconsole_read_authentication_signup get_logs read_license_information sysconsole_read_site_notices list_private_teams read_elasticsearch_post_indexing_job sysconsole_read_site_notifications sysconsole_read_authentication_mfa sysconsole_read_integrations_bot_accounts sysconsole_read_reporting_server_logs sysconsole_read_site_localization sysconsole_read_environment_elasticsearch sysconsole_read_user_management_users sysconsole_read_plugins sysconsole_read_environment_developer',0,1,NULL); +INSERT INTO `Roles` VALUES ('iiwt9pt6wiyb9e1enixtxs5yme','run_admin','authentication.roles.run_admin.name','authentication.roles.run_admin.description',1662271985864,1662271986932,0,' run_manage_properties run_manage_members',1,1,NULL); +INSERT INTO `Roles` VALUES ('jg1f1xfh3bb73pua938orwg9ie','system_guest','authentication.roles.global_guest.name','authentication.roles.global_guest.description',1605167829015,1662271986937,0,' create_direct_channel create_group_channel',1,1,NULL); +INSERT INTO `Roles` VALUES ('k891n5tpd3n9peue79azejjocy','system_post_all_public','authentication.roles.system_post_all_public.name','authentication.roles.system_post_all_public.description',0,1662271986941,0,' use_channel_mentions create_post_public',0,1,NULL); +INSERT INTO `Roles` VALUES ('kb6r9i58x7dxdb3srfohd66sse','system_admin','authentication.roles.global_admin.name','authentication.roles.global_admin.description',0,1662271986948,0,' list_public_teams edit_brand manage_private_channel_properties sysconsole_read_user_management_teams playbook_public_create manage_others_bots invalidate_caches manage_shared_channels sysconsole_write_environment_logging manage_others_outgoing_webhooks sysconsole_read_reporting_team_statistics sysconsole_read_plugins list_team_channels use_group_mentions sysconsole_read_site_users_and_teams sysconsole_write_site_localization get_analytics sysconsole_read_experimental_bleve manage_team_roles sysconsole_read_site_localization edit_post sysconsole_write_user_management_channels test_elasticsearch list_private_teams add_ldap_public_cert join_public_teams manage_slash_commands manage_others_incoming_webhooks manage_public_channel_members sysconsole_read_environment_elasticsearch sysconsole_write_site_customization delete_others_emojis run_manage_members create_emojis sysconsole_write_authentication_email sysconsole_write_compliance_compliance_export add_saml_private_cert create_bot sysconsole_write_environment_rate_limiting add_saml_public_cert edit_other_users sysconsole_write_integrations_integration_management read_user_access_token create_elasticsearch_post_indexing_job sysconsole_write_user_management_users assign_system_admin_role sysconsole_write_user_management_groups sysconsole_read_authentication_guest_access sysconsole_write_about_edition_and_license sysconsole_read_authentication_ldap sysconsole_read_experimental_feature_flags sysconsole_read_integrations_cors sysconsole_read_user_management_groups join_public_channels sysconsole_read_experimental_features test_ldap sysconsole_write_environment_elasticsearch sysconsole_write_reporting_server_logs sysconsole_read_environment_image_proxy sysconsole_read_site_announcement_banner sysconsole_read_reporting_site_statistics sysconsole_write_authentication_mfa sysconsole_read_authentication_openid purge_bleve_indexes playbook_public_manage_members delete_emojis sysconsole_write_environment_file_storage sysconsole_write_reporting_site_statistics playbook_private_manage_members import_team sysconsole_write_environment_web_server sysconsole_write_authentication_password read_public_channel_groups create_compliance_export_job sysconsole_read_authentication_password list_users_without_team sysconsole_read_authentication_mfa add_ldap_private_cert create_data_retention_job read_license_information sysconsole_write_authentication_signup sysconsole_read_environment_push_notification_server edit_others_posts download_compliance_export_result create_ldap_sync_job sysconsole_write_authentication_ldap sysconsole_write_plugins read_data_retention_job sysconsole_write_compliance_data_retention_policy sysconsole_read_site_public_links manage_bots manage_system sysconsole_write_compliance_custom_terms_of_service playbook_public_manage_roles playbook_public_manage_properties playbook_private_create sysconsole_write_experimental_bleve sysconsole_read_authentication_email promote_guest get_saml_cert_status add_user_to_team sysconsole_write_site_users_and_teams create_custom_group manage_private_channel_members read_jobs sysconsole_write_experimental_features read_other_users_teams sysconsole_write_reporting_team_statistics sysconsole_read_environment_file_storage create_post_bleve_indexes_job sysconsole_read_site_file_sharing_and_downloads playbook_private_make_public playbook_public_view create_user_access_token create_public_channel read_channel sysconsole_read_user_management_channels sysconsole_read_user_management_permissions read_public_channel sysconsole_read_compliance_custom_terms_of_service sysconsole_write_site_emoji sysconsole_read_integrations_gif sysconsole_read_site_customization sysconsole_write_integrations_cors invite_user create_direct_channel sysconsole_write_user_management_teams run_create manage_custom_group_members read_ldap_sync_job sysconsole_read_site_notifications playbook_private_manage_properties sysconsole_read_integrations_bot_accounts convert_public_channel_to_private invalidate_email_invite reload_config get_saml_metadata_from_idp manage_secure_connections delete_private_channel sysconsole_read_about_edition_and_license convert_private_channel_to_public sysconsole_read_environment_developer recycle_database_connections remove_saml_private_cert manage_oauth sysconsole_write_environment_database sysconsole_write_site_notifications sysconsole_write_authentication_guest_access sysconsole_write_compliance_compliance_monitoring sysconsole_write_environment_image_proxy create_post_public manage_jobs remove_user_from_team delete_others_posts create_post_ephemeral playbook_private_view create_elasticsearch_post_aggregation_job remove_reaction add_reaction sysconsole_write_environment_high_availability sysconsole_write_authentication_openid sysconsole_write_user_management_permissions add_saml_idp_cert sysconsole_read_site_posts view_members sysconsole_write_environment_smtp sysconsole_read_authentication_saml create_post use_channel_mentions create_team playbook_private_manage_roles get_public_link sysconsole_write_billing manage_system_wide_oauth sysconsole_read_environment_database sysconsole_write_environment_session_lengths run_manage_properties sysconsole_write_authentication_saml sysconsole_read_environment_web_server sysconsole_read_environment_rate_limiting manage_public_channel_properties create_group_channel sysconsole_read_compliance_data_retention_policy sysconsole_read_environment_high_availability manage_others_slash_commands sysconsole_read_compliance_compliance_export delete_custom_group sysconsole_read_user_management_system_roles purge_elasticsearch_indexes view_team sysconsole_read_environment_performance_monitoring manage_channel_roles playbook_public_make_private remove_saml_public_cert demote_to_guest sysconsole_write_environment_performance_monitoring read_audits sysconsole_write_site_announcement_banner upload_file revoke_user_access_token read_others_bots test_email read_elasticsearch_post_aggregation_job sysconsole_read_compliance_compliance_monitoring join_private_teams delete_post sysconsole_write_site_public_links manage_team edit_custom_group sysconsole_write_experimental_feature_flags sysconsole_write_user_management_system_roles remove_others_reactions manage_license_information sysconsole_read_authentication_signup read_compliance_export_job sysconsole_write_environment_developer remove_saml_idp_cert manage_incoming_webhooks sysconsole_read_site_emoji assign_bot sysconsole_write_integrations_gif sysconsole_read_user_management_users delete_public_channel manage_outgoing_webhooks sysconsole_write_site_posts remove_ldap_private_cert sysconsole_write_site_file_sharing_and_downloads sysconsole_read_integrations_integration_management sysconsole_read_environment_logging test_site_url sysconsole_read_environment_session_lengths read_elasticsearch_post_indexing_job sysconsole_read_billing sysconsole_read_site_notices sysconsole_read_reporting_server_logs sysconsole_write_integrations_bot_accounts sysconsole_write_site_notices create_private_channel read_private_channel_groups run_view read_bots manage_roles test_s3 sysconsole_write_environment_push_notification_server get_logs invite_guest remove_ldap_public_cert sysconsole_read_environment_smtp',1,1,NULL); +INSERT INTO `Roles` VALUES ('km7kijhdtjbajquwu36uqneyoc','system_post_all','authentication.roles.system_post_all.name','authentication.roles.system_post_all.description',0,1662271986953,0,' create_post use_channel_mentions',0,1,NULL); +INSERT INTO `Roles` VALUES ('no7s4436sjbzzqjpupg85mszty','custom_group_user','authentication.roles.custom_group_user.name','authentication.roles.custom_group_user.description',1662271985801,1662271986956,0,'',0,0,NULL); +INSERT INTO `Roles` VALUES ('qo7e17c1m3rezyjqx5iq9dpmxe','system_manager','authentication.roles.system_manager.name','authentication.roles.system_manager.description',0,1662271986960,0,' sysconsole_write_environment_image_proxy sysconsole_read_environment_developer read_ldap_sync_job sysconsole_read_reporting_team_statistics recycle_database_connections get_logs read_private_channel_groups test_elasticsearch sysconsole_read_environment_logging purge_elasticsearch_indexes sysconsole_write_site_posts sysconsole_read_environment_database sysconsole_read_environment_performance_monitoring manage_team sysconsole_read_authentication_password sysconsole_write_site_users_and_teams sysconsole_read_user_management_channels sysconsole_write_environment_rate_limiting sysconsole_write_site_notifications read_license_information edit_brand sysconsole_read_plugins sysconsole_read_environment_high_availability sysconsole_read_environment_file_storage sysconsole_read_environment_elasticsearch sysconsole_write_environment_web_server sysconsole_write_environment_smtp sysconsole_write_environment_performance_monitoring sysconsole_write_environment_session_lengths sysconsole_write_user_management_groups convert_private_channel_to_public manage_private_channel_properties sysconsole_read_site_posts list_private_teams sysconsole_read_authentication_ldap sysconsole_read_authentication_guest_access sysconsole_read_site_emoji sysconsole_write_integrations_integration_management convert_public_channel_to_private manage_private_channel_members read_elasticsearch_post_aggregation_job manage_team_roles sysconsole_write_site_file_sharing_and_downloads read_channel read_public_channel sysconsole_read_authentication_openid add_user_to_team sysconsole_write_environment_developer sysconsole_write_site_localization sysconsole_read_about_edition_and_license test_s3 reload_config sysconsole_write_environment_elasticsearch test_site_url sysconsole_write_site_announcement_banner get_analytics sysconsole_read_environment_push_notification_server sysconsole_read_authentication_signup test_email sysconsole_write_integrations_bot_accounts sysconsole_write_integrations_cors view_team sysconsole_write_integrations_gif sysconsole_read_site_notices sysconsole_read_environment_image_proxy sysconsole_read_integrations_cors sysconsole_write_environment_push_notification_server join_public_teams test_ldap create_elasticsearch_post_aggregation_job sysconsole_read_environment_session_lengths sysconsole_write_environment_file_storage manage_public_channel_members sysconsole_write_site_customization sysconsole_read_site_announcement_banner sysconsole_read_environment_smtp sysconsole_write_user_management_teams delete_public_channel sysconsole_write_environment_logging read_public_channel_groups sysconsole_read_site_users_and_teams sysconsole_read_reporting_site_statistics sysconsole_read_site_localization sysconsole_read_site_customization sysconsole_read_environment_rate_limiting sysconsole_read_environment_web_server sysconsole_write_user_management_permissions sysconsole_read_site_file_sharing_and_downloads sysconsole_write_site_public_links sysconsole_read_site_public_links sysconsole_read_authentication_email read_elasticsearch_post_indexing_job sysconsole_read_authentication_saml remove_user_from_team delete_private_channel sysconsole_write_user_management_channels sysconsole_read_reporting_server_logs sysconsole_read_integrations_bot_accounts sysconsole_read_user_management_teams list_public_teams create_elasticsearch_post_indexing_job sysconsole_write_site_emoji invalidate_caches sysconsole_read_integrations_integration_management sysconsole_write_environment_high_availability sysconsole_read_user_management_permissions join_private_teams manage_channel_roles sysconsole_write_site_notices manage_public_channel_properties sysconsole_write_environment_database sysconsole_read_site_notifications sysconsole_read_user_management_groups sysconsole_read_integrations_gif sysconsole_read_authentication_mfa',0,1,NULL); +INSERT INTO `Roles` VALUES ('rkr97ikkh7fixy86qsoo5rqm4c','system_user_access_token','authentication.roles.system_user_access_token.name','authentication.roles.system_user_access_token.description',0,1662271986965,0,' create_user_access_token read_user_access_token revoke_user_access_token',0,1,NULL); +INSERT INTO `Roles` VALUES ('rxzdk5irm7rcffcfej9e33kqeo','team_user','authentication.roles.team_user.name','authentication.roles.team_user.description',0,1662271986968,0,' invite_user view_team read_public_channel playbook_public_create add_user_to_team playbook_private_create create_private_channel list_team_channels create_public_channel join_public_channels',1,1,NULL); +INSERT INTO `Roles` VALUES ('x768jnyzw3rkfx7xb66ehcac6o','channel_user','authentication.roles.channel_user.name','authentication.roles.channel_user.description',0,1662271986972,0,' manage_public_channel_properties create_post manage_private_channel_properties delete_public_channel manage_private_channel_members get_public_link delete_post delete_private_channel upload_file edit_post remove_reaction use_channel_mentions add_reaction read_channel manage_public_channel_members',1,1,NULL); +INSERT INTO `Roles` VALUES ('ynn8aynsn7n1trtbuq6p4cyzhe','channel_guest','authentication.roles.channel_guest.name','authentication.roles.channel_guest.description',1605167829001,1662271986975,0,' read_channel add_reaction remove_reaction upload_file edit_post create_post use_channel_mentions',1,1,NULL); +INSERT INTO `Roles` VALUES ('yqyby79r9jggxg7a9dnenuawmo','run_member','authentication.roles.run_member.name','authentication.roles.run_member.description',1662271985813,1662271986979,0,' run_view',1,1,NULL); +INSERT INTO `Roles` VALUES ('zzehkfnp67bg5g1owh6eptdcxc','system_user','authentication.roles.global_user.name','authentication.roles.global_user.description',0,1662271986983,0,' create_emojis join_public_teams list_public_teams edit_custom_group delete_emojis create_team create_group_channel manage_custom_group_members view_members delete_custom_group create_custom_group create_direct_channel',1,1,NULL); /*!40000 ALTER TABLE `Roles` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; diff --git a/server/i18n/en.json b/server/i18n/en.json index 391a1d60a8..10f92b9e9c 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -6974,6 +6974,14 @@ "id": "app.role.save.invalid_role.app_error", "translation": "The role was not valid." }, + { + "id": "app.role.send_updated_role_event.app_error", + "translation": "An error occurred while broadcasting the role update." + }, + { + "id": "app.role.send_updated_role_event.unknown_scope", + "translation": "An error occurred while broadcasting the role update: unknown scheme scope." + }, { "id": "app.save_config.app_error", "translation": "An error occurred saving the configuration." diff --git a/server/public/model/role.go b/server/public/model/role.go index 4ffa9f94be..8ae0dc5e04 100644 --- a/server/public/model/role.go +++ b/server/public/model/role.go @@ -422,6 +422,7 @@ type Role struct { Permissions []string `json:"permissions"` SchemeManaged bool `json:"scheme_managed"` BuiltIn bool `json:"built_in"` + SchemeId *string `json:"scheme_id"` } func (r *Role) Auditable() map[string]any { @@ -436,6 +437,7 @@ func (r *Role) Auditable() map[string]any { "permissions": r.Permissions, "scheme_managed": r.SchemeManaged, "built_in": r.BuiltIn, + "scheme_id": r.SchemeId, } } @@ -456,6 +458,7 @@ func (r *Role) MarshalYAML() (any, error) { Permissions []string `yaml:"permissions"` SchemeManaged bool `yaml:"scheme_managed"` BuiltIn bool `yaml:"built_in"` + SchemeId *string `yaml:"scheme_id"` }{ Id: r.Id, Name: r.Name, @@ -467,6 +470,7 @@ func (r *Role) MarshalYAML() (any, error) { Permissions: r.Permissions, SchemeManaged: r.SchemeManaged, BuiltIn: r.BuiltIn, + SchemeId: r.SchemeId, }, nil } @@ -482,6 +486,7 @@ func (r *Role) UnmarshalYAML(unmarshal func(any) error) error { Permissions []string `yaml:"permissions"` SchemeManaged bool `yaml:"scheme_managed"` BuiltIn bool `yaml:"built_in"` + SchemeId *string `yaml:"scheme_id"` }{} err := unmarshal(&out) @@ -513,6 +518,7 @@ func (r *Role) UnmarshalYAML(unmarshal func(any) error) error { Permissions: out.Permissions, SchemeManaged: out.SchemeManaged, BuiltIn: out.BuiltIn, + SchemeId: out.SchemeId, } return nil }