From e1cd64613591cf5a990442a69ebf188258bd0cb5 Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Tue, 6 Feb 2018 15:34:08 +0000 Subject: XYZ-37: Advanced Permissions Phase 1 Backend. (#8159) * XYZ-13: Update Permission and Role structs to new design. * XYZ-10: Role store. * XYZ-9/XYZ-44: Roles API endpoints and WebSocket message. * XYZ-8: Switch server permissions checks to store backed roles. * XYZ-58: Proper validation of roles where required. * XYZ-11/XYZ-55: Migration to store backed roles from policy config. * XYZ-37: Update unit tests to work with database roles. * XYZ-56: Remove the "guest" role. * Changes to SetDefaultRolesFromConfig. * Short-circuit the store if nothing has changed. * Address first round of review comments. * Address second round of review comments. --- api/apitestlib.go | 112 ++++ api/channel_test.go | 728 +++------------------ api/oauth_test.go | 44 +- api/post_test.go | 59 +- api/team.go | 12 +- api/team_test.go | 100 +-- api/webhook_test.go | 143 ++-- api4/api.go | 5 + api4/apitestlib.go | 112 ++++ api4/channel.go | 1 + api4/channel_test.go | 451 ++----------- api4/context.go | 23 + api4/oauth_test.go | 100 +-- api4/params.go | 10 + api4/post_test.go | 7 - api4/role.go | 100 +++ api4/role_test.go | 184 ++++++ api4/team_test.go | 127 +--- api4/webhook_test.go | 72 +- app/app.go | 60 +- app/app_test.go | 340 +++++++++- app/apptestlib.go | 52 +- app/authorization.go | 44 +- app/authorization_test.go | 6 +- app/channel.go | 4 + app/import_test.go | 16 - app/post.go | 2 +- app/role.go | 88 ++- app/team.go | 4 + app/user.go | 4 + cmd/platform/server.go | 2 + i18n/en.json | 28 + model/authorization.go | 522 --------------- model/client4.go | 46 ++ model/cluster_message.go | 1 + model/permission.go | 418 ++++++++++++ model/permission_test.go | 18 + model/role.go | 310 +++++++++ model/user.go | 7 +- model/user_test.go | 4 +- model/websocket_message.go | 1 + store/layered_store.go | 34 + store/layered_store_supplier.go | 6 + store/local_cache_supplier.go | 8 +- store/local_cache_supplier_roles.go | 68 ++ store/redis_supplier.go | 20 + store/sqlstore/role_store_test.go | 14 + store/sqlstore/role_supplier.go | 163 +++++ store/sqlstore/store.go | 1 + store/sqlstore/supplier.go | 6 + store/store.go | 8 + store/storetest/mocks/LayeredStoreDatabaseLayer.go | 108 +++ store/storetest/mocks/LayeredStoreSupplier.go | 92 +++ store/storetest/mocks/RoleStore.go | 78 +++ store/storetest/mocks/SqlStore.go | 16 + store/storetest/mocks/Store.go | 16 + store/storetest/role_store.go | 244 +++++++ store/storetest/store.go | 3 + utils/authorization.go | 18 +- web/web_test.go | 2 + 60 files changed, 3260 insertions(+), 2012 deletions(-) create mode 100644 api4/role.go create mode 100644 api4/role_test.go delete mode 100644 model/authorization.go create mode 100644 model/permission.go create mode 100644 model/permission_test.go create mode 100644 model/role.go create mode 100644 store/local_cache_supplier_roles.go create mode 100644 store/sqlstore/role_store_test.go create mode 100644 store/sqlstore/role_supplier.go create mode 100644 store/storetest/mocks/RoleStore.go create mode 100644 store/storetest/role_store.go diff --git a/api/apitestlib.go b/api/apitestlib.go index 8d7f54902..f53e7b237 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -111,6 +111,7 @@ func setupTestHelper(enterprise bool) *TestHelper { Init(th.App, th.App.Srv.Router) wsapi.Init(th.App, th.App.Srv.WebSocketRouter) th.App.Srv.Store.MarkSystemRanUnitTests() + th.App.DoAdvancedPermissionsMigration() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) @@ -389,3 +390,114 @@ func (me *TestHelper) TearDown() { panic(err) } } + +func (me *TestHelper) SaveDefaultRolePermissions() map[string][]string { + utils.DisableDebugLogForTest() + + results := make(map[string][]string) + + for _, roleName := range []string{ + "system_user", + "system_admin", + "team_user", + "team_admin", + "channel_user", + "channel_admin", + } { + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + results[roleName] = role.Permissions + } + + utils.EnableDebugLogForTest() + return results +} + +func (me *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { + utils.DisableDebugLogForTest() + + for roleName, permissions := range data { + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") { + continue + } + + role.Permissions = permissions + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + } + + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) RemovePermissionFromRole(permission string, roleName string) { + utils.DisableDebugLogForTest() + + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + var newPermissions []string + for _, p := range role.Permissions { + if p != permission { + newPermissions = append(newPermissions, p) + } + } + + if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") { + utils.EnableDebugLogForTest() + return + } + + role.Permissions = newPermissions + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) AddPermissionToRole(permission string, roleName string) { + utils.DisableDebugLogForTest() + + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + for _, existingPermission := range role.Permissions { + if existingPermission == permission { + utils.EnableDebugLogForTest() + return + } + } + + role.Permissions = append(role.Permissions, permission) + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + + utils.EnableDebugLogForTest() +} diff --git a/api/channel_test.go b/api/channel_test.go index 5533105c3..37dde24bd 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -12,7 +12,6 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/utils" ) func TestCreateChannel(t *testing.T) { @@ -20,7 +19,6 @@ func TestCreateChannel(t *testing.T) { defer th.TearDown() Client := th.BasicClient - SystemAdminClient := th.SystemAdminClient team := th.BasicTeam th.LoginBasic2() team2 := th.CreateTeam(th.BasicClient) @@ -97,23 +95,14 @@ func TestCreateChannel(t *testing.T) { t.Fatal("Should have errored out on direct channel type") } - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetDefaultRolesBasedOnConfig() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() + + th.AddPermissionToRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) channel2 := &model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel3 := &model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -124,13 +113,10 @@ func TestCreateChannel(t *testing.T) { t.Fatal(err) } - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_ADMIN_ROLE_ID) th.LoginBasic2() channel2.Name = "zz" + model.NewId() + "a" @@ -153,50 +139,6 @@ func TestCreateChannel(t *testing.T) { if _, err := Client.CreateChannel(channel3); err != nil { t.Fatal(err) } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - channel2.Name = "zz" + model.NewId() + "a" - channel3.Name = "zz" + model.NewId() + "a" - if _, err := Client.CreateChannel(channel2); err == nil { - t.Fatal("should have errored not system admin") - } - if _, err := Client.CreateChannel(channel3); err == nil { - t.Fatal("should have errored not system admin") - } - - th.LinkUserToTeam(th.SystemAdminUser, team) - - if _, err := SystemAdminClient.CreateChannel(channel2); err != nil { - t.Fatal(err) - } - if _, err := SystemAdminClient.CreateChannel(channel3); err != nil { - t.Fatal(err) - } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - channel4 := model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel5 := model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - if _, err := Client.CreateChannel(&channel4); err != nil { - t.Fatal("should have succeeded") - } - if _, err := Client.CreateChannel(&channel5); err != nil { - t.Fatal("should have succeeded") - } - - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_ALL }) - th.App.SetDefaultRolesBasedOnConfig() } func TestCreateDirectChannel(t *testing.T) { @@ -367,23 +309,17 @@ func TestUpdateChannel(t *testing.T) { t.Fatal("should have failed - channel deleted") } - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) channel2 := th.CreateChannel(Client, team) channel3 := th.CreatePrivateChannel(Client, team) @@ -402,16 +338,11 @@ func TestUpdateChannel(t *testing.T) { t.Fatal(err) } - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -423,17 +354,6 @@ func TestUpdateChannel(t *testing.T) { t.Fatal("should have errored not channel admin") } - th.UpdateUserToTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - if _, err := Client.UpdateChannel(channel2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannel(channel3); err != nil { - t.Fatal(err) - } - th.UpdateUserToNonTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - th.MakeUserChannelAdmin(th.BasicUser, channel2) th.MakeUserChannelAdmin(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -444,75 +364,6 @@ func TestUpdateChannel(t *testing.T) { if _, err := Client.UpdateChannel(channel3); err != nil { t.Fatal(err) } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannel(channel2); err == nil { - t.Fatal("should have errored not team admin") - } - if _, err := Client.UpdateChannel(channel3); err == nil { - t.Fatal("should have errored not team admin") - } - - th.UpdateUserToTeamAdmin(th.BasicUser, team) - Client.Logout() - Client.Login(th.BasicUser.Email, th.BasicUser.Password) - Client.SetTeamId(team.Id) - - if _, err := Client.UpdateChannel(channel2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannel(channel3); err != nil { - t.Fatal(err) - } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannel(channel2); err == nil { - t.Fatal("should have errored not system admin") - } - if _, err := Client.UpdateChannel(channel3); err == nil { - t.Fatal("should have errored not system admin") - } - - th.LoginSystemAdmin() - - if _, err := Client.UpdateChannel(channel2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannel(channel3); err != nil { - t.Fatal(err) - } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannel(channel2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannel(channel3); err != nil { - t.Fatal(err) - } } func TestUpdateChannelDisplayName(t *testing.T) { @@ -548,7 +399,6 @@ func TestUpdateChannelHeader(t *testing.T) { defer th.TearDown() Client := th.BasicClient - SystemAdminClient := th.SystemAdminClient team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} @@ -618,23 +468,17 @@ func TestUpdateChannelHeader(t *testing.T) { t.Fatal("should have errored non-channel member trying to update header") } - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -657,13 +501,11 @@ func TestUpdateChannelHeader(t *testing.T) { t.Fatal(err) } - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -685,72 +527,6 @@ func TestUpdateChannelHeader(t *testing.T) { if _, err := Client.UpdateChannelHeader(data3); err != nil { t.Fatal(err) } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannelHeader(data2); err == nil { - t.Fatal("should have errored not team admin") - } - if _, err := Client.UpdateChannelHeader(data3); err == nil { - t.Fatal("should have errored not team admin") - } - - th.UpdateUserToTeamAdmin(th.BasicUser, team) - Client.Logout() - th.LoginBasic() - Client.SetTeamId(team.Id) - - if _, err := Client.UpdateChannelHeader(data2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannelHeader(data3); err != nil { - t.Fatal(err) - } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannelHeader(data2); err == nil { - t.Fatal("should have errored not system admin") - } - if _, err := Client.UpdateChannelHeader(data3); err == nil { - t.Fatal("should have errored not system admin") - } - - th.LinkUserToTeam(th.SystemAdminUser, team) - Client.Must(Client.AddChannelMember(channel2.Id, th.SystemAdminUser.Id)) - Client.Must(Client.AddChannelMember(channel3.Id, th.SystemAdminUser.Id)) - th.LoginSystemAdmin() - - if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { - t.Fatal(err) - } - if _, err := SystemAdminClient.UpdateChannelHeader(data3); err != nil { - t.Fatal(err) - } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { - t.Fatal(err) - } - if _, err := SystemAdminClient.UpdateChannelHeader(data3); err != nil { - t.Fatal(err) - } } func TestUpdateChannelPurpose(t *testing.T) { @@ -758,7 +534,6 @@ func TestUpdateChannelPurpose(t *testing.T) { defer th.TearDown() Client := th.BasicClient - SystemAdminClient := th.SystemAdminClient team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} @@ -814,23 +589,17 @@ func TestUpdateChannelPurpose(t *testing.T) { t.Fatal("should have errored non-channel member trying to update purpose") } - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.TEAM_USER_ROLE_ID) + + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -853,13 +622,11 @@ func TestUpdateChannelPurpose(t *testing.T) { t.Fatal(err) } - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -881,71 +648,6 @@ func TestUpdateChannelPurpose(t *testing.T) { if _, err := Client.UpdateChannelPurpose(data3); err != nil { t.Fatal(err) } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannelPurpose(data2); err == nil { - t.Fatal("should have errored not team admin") - } - if _, err := Client.UpdateChannelPurpose(data3); err == nil { - t.Fatal("should have errored not team admin") - } - - th.UpdateUserToTeamAdmin(th.BasicUser, team) - Client.Logout() - th.LoginBasic() - Client.SetTeamId(team.Id) - - if _, err := Client.UpdateChannelPurpose(data2); err != nil { - t.Fatal(err) - } - if _, err := Client.UpdateChannelPurpose(data3); err != nil { - t.Fatal(err) - } - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.UpdateChannelPurpose(data2); err == nil { - t.Fatal("should have errored not system admin") - } - if _, err := Client.UpdateChannelPurpose(data3); err == nil { - t.Fatal("should have errored not system admin") - } - - th.LinkUserToTeam(th.SystemAdminUser, team) - Client.Must(Client.AddChannelMember(channel2.Id, th.SystemAdminUser.Id)) - Client.Must(Client.AddChannelMember(channel3.Id, th.SystemAdminUser.Id)) - th.LoginSystemAdmin() - - if _, err := SystemAdminClient.UpdateChannelPurpose(data2); err != nil { - t.Fatal(err) - } - if _, err := SystemAdminClient.UpdateChannelPurpose(data3); err != nil { - t.Fatal(err) - } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { - t.Fatal(err) - } - if _, err := SystemAdminClient.UpdateChannelHeader(data3); err != nil { - t.Fatal(err) - } } func TestGetChannel(t *testing.T) { @@ -1400,23 +1102,14 @@ func TestDeleteChannel(t *testing.T) { t.Fatal("should have failed - channel already deleted") } - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.AddPermissionToRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) th.LoginSystemAdmin() th.LinkUserToTeam(th.BasicUser, team) @@ -1438,16 +1131,10 @@ func TestDeleteChannel(t *testing.T) { t.Fatal(err) } - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) th.LoginSystemAdmin() @@ -1478,78 +1165,6 @@ func TestDeleteChannel(t *testing.T) { th.LoginSystemAdmin() - channel2 = th.CreateChannel(Client, team) - channel3 = th.CreatePrivateChannel(Client, team) - - Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) - Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) - th.UpdateUserToTeamAdmin(th.BasicUser, team) - - Client.Login(th.BasicUser.Email, th.BasicUser.Password) - th.App.InvalidateAllCaches() - - if _, err := Client.DeleteChannel(channel2.Id); err != nil { - t.Fatal(err) - } - if _, err := Client.DeleteChannel(channel3.Id); err != nil { - t.Fatal(err) - } - - th.UpdateUserToNonTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - th.LoginSystemAdmin() - - channel2 = th.CreateChannel(Client, team) - channel3 = th.CreatePrivateChannel(Client, team) - Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) - Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) - - Client.Login(th.BasicUser.Email, th.BasicUser.Password) - - if _, err := Client.DeleteChannel(channel2.Id); err == nil { - t.Fatal("should have errored not team admin") - } - if _, err := Client.DeleteChannel(channel3.Id); err == nil { - t.Fatal("should have errored not team admin") - } - - th.UpdateUserToTeamAdmin(th.BasicUser, team) - Client.Logout() - Client.Login(th.BasicUser.Email, th.BasicUser.Password) - Client.SetTeamId(team.Id) - - if _, err := Client.DeleteChannel(channel2.Id); err != nil { - t.Fatal(err) - } - if _, err := Client.DeleteChannel(channel3.Id); err != nil { - t.Fatal(err) - } - - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - th.LoginSystemAdmin() - channel2 = th.CreateChannel(Client, team) channel3 = th.CreatePrivateChannel(Client, team) Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) @@ -1576,29 +1191,6 @@ func TestDeleteChannel(t *testing.T) { if _, err := Client.DeleteChannel(channel3.Id); err != nil { t.Fatal(err) } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - channel2 = th.CreateChannel(Client, team) - channel3 = th.CreatePrivateChannel(Client, team) - Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) - Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) - - Client.Login(th.BasicUser.Email, th.BasicUser.Password) - - if _, err := Client.DeleteChannel(channel2.Id); err != nil { - t.Fatal(err) - } - if _, err := Client.DeleteChannel(channel3.Id); err != nil { - t.Fatal(err) - } - - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_ALL }) - th.App.SetDefaultRolesBasedOnConfig() } func TestGetChannelStats(t *testing.T) { @@ -1674,38 +1266,14 @@ func TestAddChannelMember(t *testing.T) { t.Fatal("Should have errored, user not on team") } - // Test policy does not apply to TE. - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel - }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - channel3 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel3 = Client.Must(th.SystemAdminClient.CreateChannel(channel3)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel3.Id, user1.Id)) - if _, err := Client.AddChannelMember(channel3.Id, user2.Id); err != nil { - t.Fatal(err) - } - - // Add a license - isLicensed := utils.IsLicensed() - license := utils.License() - defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) // Check that a regular channel user can add other users. channel4 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -1715,14 +1283,10 @@ func TestAddChannelMember(t *testing.T) { t.Fatal(err) } - // Test with CHANNEL_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) channel5 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel5 = Client.Must(th.SystemAdminClient.CreateChannel(channel5)).Data.(*model.Channel) @@ -1733,61 +1297,10 @@ func TestAddChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, channel5) th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.AddChannelMember(channel5.Id, user2.Id); err != nil { t.Fatal(err) } - - // Test with TEAM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - channel6 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel6 = Client.Must(th.SystemAdminClient.CreateChannel(channel6)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel6.Id, user1.Id)) - if _, err := Client.AddChannelMember(channel6.Id, user2.Id); err == nil { - t.Fatal("Should have failed due to permissions") - } - - th.UpdateUserToTeamAdmin(user1, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.AddChannelMember(channel6.Id, user2.Id); err != nil { - t.Fatal(err) - } - - // Test with SYSTEM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - channel7 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel7 = Client.Must(th.SystemAdminClient.CreateChannel(channel7)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel7.Id, user1.Id)) - if _, err := Client.AddChannelMember(channel7.Id, user2.Id); err == nil { - t.Fatal("Should have failed due to permissions") - } - - if _, err := th.SystemAdminClient.AddChannelMember(channel7.Id, user2.Id); err != nil { - t.Fatal(err) - } } func TestRemoveChannelMember(t *testing.T) { @@ -1859,39 +1372,14 @@ func TestRemoveChannelMember(t *testing.T) { th.LoginBasic() - // Test policy does not apply to TE. - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel - }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - channel3 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel3 = Client.Must(th.SystemAdminClient.CreateChannel(channel3)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel3.Id, user1.Id)) - Client.Must(th.SystemAdminClient.AddChannelMember(channel3.Id, user2.Id)) - if _, err := Client.RemoveChannelMember(channel3.Id, user2.Id); err != nil { - t.Fatal(err) - } - - // Add a license - isLicensed := utils.IsLicensed() - license := utils.License() - defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) // Check that a regular channel user can remove other users. channel4 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -1902,14 +1390,10 @@ func TestRemoveChannelMember(t *testing.T) { t.Fatal(err) } - // Test with CHANNEL_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) channel5 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel5 = Client.Must(th.SystemAdminClient.CreateChannel(channel5)).Data.(*model.Channel) @@ -1921,62 +1405,10 @@ func TestRemoveChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, channel5) th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() if _, err := Client.RemoveChannelMember(channel5.Id, user2.Id); err != nil { t.Fatal(err) } - - // Test with TEAM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - channel6 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel6 = Client.Must(th.SystemAdminClient.CreateChannel(channel6)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel6.Id, user1.Id)) - Client.Must(th.SystemAdminClient.AddChannelMember(channel6.Id, user2.Id)) - if _, err := Client.RemoveChannelMember(channel6.Id, user2.Id); err == nil { - t.Fatal("Should have failed due to permissions") - } - - th.UpdateUserToTeamAdmin(user1, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.RemoveChannelMember(channel6.Id, user2.Id); err != nil { - t.Fatal(err) - } - - // Test with SYSTEM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - channel7 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel7 = Client.Must(th.SystemAdminClient.CreateChannel(channel7)).Data.(*model.Channel) - Client.Must(th.SystemAdminClient.AddChannelMember(channel7.Id, user1.Id)) - Client.Must(th.SystemAdminClient.AddChannelMember(channel7.Id, user2.Id)) - if _, err := Client.RemoveChannelMember(channel7.Id, user2.Id); err == nil { - t.Fatal("Should have failed due to permissions") - } - - if _, err := th.SystemAdminClient.RemoveChannelMember(channel7.Id, user2.Id); err != nil { - t.Fatal(err) - } } func TestUpdateNotifyProps(t *testing.T) { diff --git a/api/oauth_test.go b/api/oauth_test.go index ec1e557da..2663c784b 100644 --- a/api/oauth_test.go +++ b/api/oauth_test.go @@ -88,7 +88,12 @@ func TestOAuthRegisterApp(t *testing.T) { t.Fatal("should have failed. not enough permissions") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) th.LoginBasic() @@ -210,7 +215,12 @@ func TestOAuthGetAppsByUser(t *testing.T) { t.Fatal("Should have failed.") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) if result, err := Client.GetOAuthAppsByUser(); err != nil { t.Fatal(err) @@ -250,7 +260,9 @@ func TestOAuthGetAppsByUser(t *testing.T) { user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true} ruser := Client.Must(AdminClient.CreateUser(user, "")).Data.(*model.User) - th.App.UpdateUserRoles(ruser.Id, "", false) + if _, err := th.App.UpdateUserRoles(ruser.Id, "", false); err != nil { + t.Fatal(err) + } Client.Logout() Client.Login(user.Email, user.Password) @@ -437,7 +449,13 @@ func TestOAuthDeleteApp(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -492,13 +510,17 @@ func TestOAuthAccessToken(t *testing.T) { Client := th.BasicClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp) @@ -739,7 +761,13 @@ func TestOAuthComplete(t *testing.T) { // We are going to use mattermost as the provider emulating gitlab th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oauthApp := &model.OAuthApp{ Name: "TestApp5" + model.NewId(), diff --git a/api/post_test.go b/api/post_test.go index 299fdf046..fe0c2a6b7 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -167,10 +167,8 @@ func TestCreatePost(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = disableTownSquareReadOnly }) utils.SetIsLicensed(isLicensed) utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true }) - th.App.SetDefaultRolesBasedOnConfig() utils.SetIsLicensed(true) utils.SetLicense(&model.License{Features: &model.Features{}}) utils.License().Features.SetDefaults() @@ -965,9 +963,6 @@ func TestDeletePosts(t *testing.T) { channel1 := th.BasicChannel team1 := th.BasicTeam - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_ALL }) - th.App.SetDefaultRolesBasedOnConfig() - time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) @@ -1020,16 +1015,11 @@ func TestDeletePosts(t *testing.T) { t.Fatal(err) } - // Test licensed policy controls for delete post - isLicensed := utils.IsLicensed() - license := utils.License() + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) @@ -1041,10 +1031,8 @@ func TestDeletePosts(t *testing.T) { SystemAdminClient.Must(SystemAdminClient.DeletePost(channel1.Id, post4b.Id)) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.RemovePermissionFromRole(model.PERMISSION_DELETE_POST.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_POST.Id, model.TEAM_ADMIN_ROLE_ID) th.LoginBasic() @@ -1065,43 +1053,6 @@ func TestDeletePosts(t *testing.T) { Client.Must(Client.DeletePost(channel1.Id, post5a.Id)) SystemAdminClient.Must(SystemAdminClient.DeletePost(channel1.Id, post5b.Id)) - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - th.LoginBasic() - - time.Sleep(10 * time.Millisecond) - post6a := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"} - post6a = Client.Must(Client.CreatePost(post6a)).Data.(*model.Post) - - if _, err := Client.DeletePost(channel1.Id, post6a.Id); err == nil { - t.Fatal(err) - } - - th.LoginBasic2() - - if _, err := Client.DeletePost(channel1.Id, post6a.Id); err == nil { - t.Fatal(err) - } - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - time.Sleep(10 * time.Millisecond) - post7 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"} - post7 = Client.Must(Client.CreatePost(post7)).Data.(*model.Post) - - if _, err := Client.DeletePost(channel1.Id, post7.Id); err != nil { - t.Fatal(err) - } - - SystemAdminClient.Must(SystemAdminClient.DeletePost(channel1.Id, post6a.Id)) - } func TestEmailMention(t *testing.T) { diff --git a/api/team.go b/api/team.go index 48377f970..e89f368ec 100644 --- a/api/team.go +++ b/api/team.go @@ -13,7 +13,6 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/utils" ) func (api *API) InitTeam() { @@ -117,15 +116,8 @@ func getAll(c *Context, w http.ResponseWriter, r *http.Request) { func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { invites := model.InvitesFromJson(r.Body) - if utils.IsLicensed() && !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_INVITE_USER) { - errorId := "" - if *c.App.Config().TeamSettings.RestrictTeamInvite == model.PERMISSIONS_SYSTEM_ADMIN { - errorId = "api.team.invite_members.restricted_system_admin.app_error" - } else if *c.App.Config().TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN { - errorId = "api.team.invite_members.restricted_team_admin.app_error" - } - - c.Err = model.NewAppError("inviteMembers", errorId, nil, "", http.StatusForbidden) + if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_INVITE_USER) { + c.SetPermissionError(model.PERMISSION_INVITE_USER) return } diff --git a/api/team_test.go b/api/team_test.go index b1c892544..3db454b62 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -139,32 +139,17 @@ func TestAddUserToTeam(t *testing.T) { t.Fatal(err) } - // Restore config/license at end of test case. - restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite - isLicensed := utils.IsLicensed() - license := utils.License() + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() // Set the config so that only team admins can add a user to a team. - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - - // Test without the EE license to see that the permission restriction is ignored. - user3 := th.CreateUser(th.BasicClient) - if _, err := th.BasicClient.AddUserToTeam(th.BasicTeam.Id, user3.Id); err != nil { - t.Fatal(err) - } - - // Add an EE license. - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) // Check that a regular user can't add someone to the team. user4 := th.CreateUser(th.BasicClient) @@ -175,32 +160,17 @@ func TestAddUserToTeam(t *testing.T) { // Should work as team admin. th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + // Change permission level to team user + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) user5 := th.CreateUser(th.BasicClient) if _, err := th.BasicClient.AddUserToTeam(th.BasicTeam.Id, user5.Id); err != nil { t.Fatal(err) } - - // Change permission level to System Admin - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - - // Should not work as team admin. - user6 := th.CreateUser(th.BasicClient) - if _, err := th.BasicClient.AddUserToTeam(th.BasicTeam.Id, user6.Id); err == nil { - t.Fatal("should have failed due to permissions error") - } - - // Should work as system admin. - user7 := th.CreateUser(th.BasicClient) - if _, err := th.SystemAdminClient.AddUserToTeam(th.BasicTeam.Id, user7.Id); err != nil { - t.Fatal(err) - } } func TestRemoveUserFromTeam(t *testing.T) { @@ -534,7 +504,6 @@ func TestInviteMembers(t *testing.T) { defer th.TearDown() Client := th.BasicClient - SystemAdminClient := th.SystemAdminClient team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) @@ -565,33 +534,21 @@ func TestInviteMembers(t *testing.T) { t.Fatal("Should have errored out on no invites to send") } - restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() + + // Set the config so that only team admins can add a user to a team. + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) th.LoginBasic2() th.LinkUserToTeam(th.BasicUser2, team) - if _, err := Client.InviteMembers(invites); err != nil { - t.Fatal(err) - } - - isLicensed := utils.IsLicensed() - license := utils.License() - defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - }() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - if _, err := Client.InviteMembers(invites); err == nil { t.Fatal("should have errored not team admin and licensed") } @@ -604,19 +561,6 @@ func TestInviteMembers(t *testing.T) { if _, err := Client.InviteMembers(invites); err != nil { t.Fatal(err) } - - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - - if _, err := Client.InviteMembers(invites); err == nil { - t.Fatal("should have errored not system admin and licensed") - } - - th.LinkUserToTeam(th.SystemAdminUser, team) - - if _, err := SystemAdminClient.InviteMembers(invites); err != nil { - t.Fatal(err) - } } func TestUpdateTeamDisplayName(t *testing.T) { diff --git a/api/webhook_test.go b/api/webhook_test.go index b5a836603..b6b754ad3 100644 --- a/api/webhook_test.go +++ b/api/webhook_test.go @@ -25,8 +25,16 @@ func TestCreateIncomingHook(t *testing.T) { user2 := th.CreateUser(Client) th.LinkUserToTeam(user2, team) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.IncomingWebhook{ChannelId: channel1.Id} @@ -92,8 +100,8 @@ func TestCreateIncomingHook(t *testing.T) { t.Fatal(err) } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.CreateIncomingWebhook(hook); err != nil { t.Fatal(err) @@ -132,8 +140,12 @@ func TestUpdateIncomingHook(t *testing.T) { th.UpdateUserToTeamAdmin(user3, team2) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) hook := createIncomingWebhook(channel1.Id, Client, t) @@ -216,8 +228,9 @@ func TestUpdateIncomingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) t.Run("OnlyAdminIntegrationsDisabled", func(t *testing.T) { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) t.Run("UpdateHookOfSameUser", func(t *testing.T) { sameUserHook := &model.IncomingWebhook{ChannelId: channel1.Id, UserId: user2.Id} @@ -239,8 +252,8 @@ func TestUpdateIncomingHook(t *testing.T) { }) }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) Client.Logout() th.UpdateUserToTeamAdmin(user2, team) @@ -323,8 +336,15 @@ func TestListIncomingHooks(t *testing.T) { th.LinkUserToTeam(user2, team) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook1 := &model.IncomingWebhook{ChannelId: channel1.Id} hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook) @@ -350,8 +370,8 @@ func TestListIncomingHooks(t *testing.T) { t.Fatal("should have errored - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.ListIncomingWebhooks(); err != nil { t.Fatal(err) @@ -375,8 +395,15 @@ func TestDeleteIncomingHook(t *testing.T) { th.LinkUserToTeam(user2, team) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.IncomingWebhook{ChannelId: channel1.Id} hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) @@ -409,8 +436,8 @@ func TestDeleteIncomingHook(t *testing.T) { t.Fatal("should have failed - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -446,8 +473,15 @@ func TestCreateOutgoingHook(t *testing.T) { th.LinkUserToTeam(user3, team2) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} @@ -517,8 +551,8 @@ func TestCreateOutgoingHook(t *testing.T) { t.Fatal("should have failed - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.CreateOutgoingWebhook(hook); err != nil { t.Fatal(err) @@ -550,8 +584,15 @@ func TestListOutgoingHooks(t *testing.T) { th.LinkUserToTeam(user2, team) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook) @@ -577,8 +618,8 @@ func TestListOutgoingHooks(t *testing.T) { t.Fatal("should have failed - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.ListOutgoingWebhooks(); err != nil { t.Fatal(err) @@ -608,8 +649,15 @@ func TestUpdateOutgoingHook(t *testing.T) { th.LinkUserToTeam(user3, team2) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"cats"}, Client, t) createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"dogs"}, Client, t) @@ -682,16 +730,17 @@ func TestUpdateOutgoingHook(t *testing.T) { t.Fatal("should have failed - user does not have permissions to manage webhooks") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + hook2 := createOutgoingWebhook(channel1.Id, []string{"http://nowhereelse.com"}, []string{"dogs"}, Client, t) if _, err := Client.UpdateOutgoingWebhook(hook2); err != nil { t.Fatal("update webhook failed when admin only integrations is turned off") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) Client.Logout() th.LinkUserToTeam(user3, team) @@ -778,8 +827,15 @@ func TestDeleteOutgoingHook(t *testing.T) { th.LinkUserToTeam(user2, team) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -812,8 +868,8 @@ func TestDeleteOutgoingHook(t *testing.T) { t.Fatal("should have failed - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -847,8 +903,15 @@ func TestRegenOutgoingHookToken(t *testing.T) { th.LinkUserToTeam(user3, team2) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -882,8 +945,8 @@ func TestRegenOutgoingHookToken(t *testing.T) { t.Fatal("should have failed - not system/team admin") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -969,10 +1032,8 @@ func TestIncomingWebhooks(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = disableTownSquareReadOnly }) utils.SetIsLicensed(isLicensed) utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true }) - th.App.SetDefaultRolesBasedOnConfig() utils.SetIsLicensed(true) utils.SetLicense(&model.License{Features: &model.Features{}}) utils.License().Features.SetDefaults() diff --git a/api4/api.go b/api4/api.go index 580bd8c58..583a6041b 100644 --- a/api4/api.go +++ b/api4/api.go @@ -96,6 +96,8 @@ type Routes struct { Reactions *mux.Router // 'api/v4/reactions' + Roles *mux.Router // 'api/v4/roles' + Emojis *mux.Router // 'api/v4/emoji' Emoji *mux.Router // 'api/v4/emoji/{emoji_id:[A-Za-z0-9]+}' EmojiByName *mux.Router // 'api/v4/emoji/name/{emoji_name:[A-Za-z0-9_-\.]+}' @@ -194,6 +196,8 @@ func Init(a *app.App, root *mux.Router, full bool) *API { api.BaseRoutes.OpenGraph = api.BaseRoutes.ApiRoot.PathPrefix("/opengraph").Subrouter() + api.BaseRoutes.Roles = api.BaseRoutes.ApiRoot.PathPrefix("/roles").Subrouter() + api.InitUser() api.InitTeam() api.InitChannel() @@ -219,6 +223,7 @@ func Init(a *app.App, root *mux.Router, full bool) *API { api.InitWebrtc() api.InitOpenGraph() api.InitPlugin() + api.InitRole() root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(Handle404)) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index a7e64ae84..bdca072c5 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -118,6 +118,7 @@ func setupTestHelper(enterprise bool) *TestHelper { Init(th.App, th.App.Srv.Router, true) wsapi.Init(th.App, th.App.Srv.WebSocketRouter) th.App.Srv.Store.MarkSystemRanUnitTests() + th.App.DoAdvancedPermissionsMigration() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) @@ -799,3 +800,114 @@ func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Tea } utils.EnableDebugLogForTest() } + +func (me *TestHelper) SaveDefaultRolePermissions() map[string][]string { + utils.DisableDebugLogForTest() + + results := make(map[string][]string) + + for _, roleName := range []string{ + "system_user", + "system_admin", + "team_user", + "team_admin", + "channel_user", + "channel_admin", + } { + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + results[roleName] = role.Permissions + } + + utils.EnableDebugLogForTest() + return results +} + +func (me *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { + utils.DisableDebugLogForTest() + + for roleName, permissions := range data { + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") { + continue + } + + role.Permissions = permissions + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + } + + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) RemovePermissionFromRole(permission string, roleName string) { + utils.DisableDebugLogForTest() + + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + var newPermissions []string + for _, p := range role.Permissions { + if p != permission { + newPermissions = append(newPermissions, p) + } + } + + if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") { + utils.EnableDebugLogForTest() + return + } + + role.Permissions = newPermissions + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) AddPermissionToRole(permission string, roleName string) { + utils.DisableDebugLogForTest() + + role, err1 := me.App.GetRoleByName(roleName) + if err1 != nil { + utils.EnableDebugLogForTest() + panic(err1) + } + + for _, existingPermission := range role.Permissions { + if existingPermission == permission { + utils.EnableDebugLogForTest() + return + } + } + + role.Permissions = append(role.Permissions, permission) + + _, err2 := me.App.UpdateRole(role) + if err2 != nil { + utils.EnableDebugLogForTest() + panic(err2) + } + + utils.EnableDebugLogForTest() +} diff --git a/api4/channel.go b/api4/channel.go index 9801c9dc0..d46c3d559 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -7,6 +7,7 @@ import ( "net/http" l4g "github.com/alecthomas/log4go" + "github.com/mattermost/mattermost-server/model" ) diff --git a/api4/channel_test.go b/api4/channel_test.go index 724b0d84b..4deceb4c4 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -14,7 +14,6 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/utils" ) func TestCreateChannel(t *testing.T) { @@ -79,26 +78,16 @@ func TestCreateChannel(t *testing.T) { _, resp = Client.CreateChannel(private) CheckForbiddenStatus(t, resp) - th.LoginBasic() - - // Check permissions with policy config changes - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelCreation - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelCreation + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.AddPermissionToRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + + th.LoginBasic() channel.Name = GenerateTestChannelName() _, resp = Client.CreateChannel(channel) @@ -108,13 +97,10 @@ func TestCreateChannel(t *testing.T) { _, resp = Client.CreateChannel(private) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) _, resp = Client.CreateChannel(channel) CheckForbiddenStatus(t, resp) @@ -140,51 +126,7 @@ func TestCreateChannel(t *testing.T) { _, resp = th.SystemAdminClient.CreateChannel(private) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - th.LoginBasic() - - _, resp = Client.CreateChannel(channel) - CheckForbiddenStatus(t, resp) - - _, resp = Client.CreateChannel(private) - CheckForbiddenStatus(t, resp) - - th.LoginTeamAdmin() - - _, resp = Client.CreateChannel(channel) - CheckForbiddenStatus(t, resp) - - _, resp = Client.CreateChannel(private) - CheckForbiddenStatus(t, resp) - - channel.Name = GenerateTestChannelName() - _, resp = th.SystemAdminClient.CreateChannel(channel) - CheckNoError(t, resp) - - private.Name = GenerateTestChannelName() - _, resp = th.SystemAdminClient.CreateChannel(private) - CheckNoError(t, resp) - - // Check that if unlicensed the policy restriction is not enforced. - utils.SetIsLicensed(false) - utils.SetLicense(nil) - th.App.SetDefaultRolesBasedOnConfig() - - channel.Name = GenerateTestChannelName() - _, resp = Client.CreateChannel(channel) - CheckNoError(t, resp) - - private.Name = GenerateTestChannelName() - _, resp = Client.CreateChannel(private) - CheckNoError(t, resp) - + // Test posting Garbage if r, err := Client.DoApiPost("/channels", "garbage"); err == nil { t.Fatal("should have errored") } else { @@ -887,23 +829,16 @@ func TestDeleteChannel(t *testing.T) { th.InitBasic().InitSystemAdmin() - isLicensed := utils.IsLicensed() - license := utils.License() - restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + + th.AddPermissionToRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.TEAM_USER_ROLE_ID) Client = th.Client team = th.BasicTeam @@ -924,13 +859,11 @@ func TestDeleteChannel(t *testing.T) { _, resp = Client.DeleteChannel(privateChannel7.Id) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() + // Restrict permissions to Channel Admins + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.CHANNEL_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) // channels created by SystemAdmin publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) @@ -957,129 +890,9 @@ func TestDeleteChannel(t *testing.T) { _, resp = Client.DeleteChannel(privateChannel7.Id) CheckNoError(t, resp) - // // channels created by SystemAdmin - publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) - privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - th.App.AddUserToChannel(user, publicChannel6) - th.App.AddUserToChannel(user, privateChannel7) - th.App.AddUserToChannel(user2, privateChannel7) - - // successful delete by team admin - th.UpdateUserToTeamAdmin(user, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckNoError(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckNoError(t, resp) - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - th.UpdateUserToNonTeamAdmin(user, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - - // channels created by SystemAdmin - publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) - privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - th.App.AddUserToChannel(user, publicChannel6) - th.App.AddUserToChannel(user, privateChannel7) - th.App.AddUserToChannel(user2, privateChannel7) - - // cannot delete by user - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // // cannot delete by channel admin - th.MakeUserChannelAdmin(user, publicChannel6) - th.MakeUserChannelAdmin(user, privateChannel7) - sqlstore.ClearChannelCaches() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // successful delete by team admin - th.UpdateUserToTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckNoError(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckNoError(t, resp) - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - // channels created by SystemAdmin - publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) - privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - th.App.AddUserToChannel(user, publicChannel6) - th.App.AddUserToChannel(user, privateChannel7) - th.App.AddUserToChannel(user2, privateChannel7) - - // cannot delete by user - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // cannot delete by channel admin - th.MakeUserChannelAdmin(user, publicChannel6) - th.MakeUserChannelAdmin(user, privateChannel7) - sqlstore.ClearChannelCaches() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // cannot delete by team admin - th.UpdateUserToTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // successful delete by SystemAdmin - _, resp = th.SystemAdminClient.DeleteChannel(publicChannel6.Id) - CheckNoError(t, resp) - - _, resp = th.SystemAdminClient.DeleteChannel(privateChannel7.Id) - CheckNoError(t, resp) + // Make sure team admins don't have permission to delete channels. + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, model.CHANNEL_ADMIN_ROLE_ID) // last member of a public channel should have required permission to delete publicChannel6 = th.CreateChannelWithClient(th.Client, model.CHANNEL_OPEN) @@ -1822,42 +1635,13 @@ func TestAddChannelMember(t *testing.T) { _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) CheckNoError(t, resp) - // Test policy does not apply to TE. - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel - }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - Client.Login(user2.Username, user2.Password) - privateChannel = th.CreatePrivateChannel() - _, resp = Client.AddChannelMember(privateChannel.Id, user.Id) - CheckNoError(t, resp) - Client.Logout() - Client.Login(user.Username, user.Password) - _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) - CheckNoError(t, resp) - Client.Logout() - - // Add a license - isLicensed := utils.IsLicensed() - license := utils.License() - defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) // Check that a regular channel user can add other users. Client.Login(user2.Username, user2.Password) @@ -1871,14 +1655,9 @@ func TestAddChannelMember(t *testing.T) { CheckNoError(t, resp) Client.Logout() - // Test with CHANNEL_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + // Restrict the permission for adding users to Channel Admins + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) Client.Login(user2.Username, user2.Password) privateChannel = th.CreatePrivateChannel() @@ -1893,70 +1672,11 @@ func TestAddChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user, privateChannel) th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - Client.Login(user.Username, user.Password) - _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) - CheckNoError(t, resp) - Client.Logout() - - // Test with TEAM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - Client.Login(user2.Username, user2.Password) - privateChannel = th.CreatePrivateChannel() - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user.Id) - CheckNoError(t, resp) - Client.Logout() - - Client.Login(user.Username, user.Password) - _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) - CheckForbiddenStatus(t, resp) - Client.Logout() - - th.UpdateUserToTeamAdmin(user, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() Client.Login(user.Username, user.Password) _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) CheckNoError(t, resp) Client.Logout() - - // Test with SYSTEM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - Client.Login(user2.Username, user2.Password) - privateChannel = th.CreatePrivateChannel() - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user.Id) - CheckNoError(t, resp) - Client.Logout() - - Client.Login(user.Username, user.Password) - _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) - CheckForbiddenStatus(t, resp) - Client.Logout() - - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user3.Id) - CheckNoError(t, resp) } func TestRemoveChannelMember(t *testing.T) { @@ -2018,43 +1738,16 @@ func TestRemoveChannelMember(t *testing.T) { th.UpdateUserToNonTeamAdmin(user1, team) th.App.InvalidateAllCaches() - // Test policy does not apply to TE. - restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel - }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - th.App.SetDefaultRolesBasedOnConfig() - - privateChannel := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) - CheckNoError(t, resp) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - // Add a license - isLicensed := utils.IsLicensed() - license := utils.License() - defer func() { - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) // Check that a regular channel user can remove other users. - privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + privateChannel := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) CheckNoError(t, resp) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) @@ -2063,14 +1756,9 @@ func TestRemoveChannelMember(t *testing.T) { _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) CheckNoError(t, resp) - // Test with CHANNEL_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() + // Restrict the permission for adding users to Channel Admins + th.AddPermissionToRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, model.CHANNEL_USER_ROLE_ID) privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) @@ -2083,58 +1771,7 @@ func TestRemoveChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, privateChannel) th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - - _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - // Test with TEAM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) - CheckNoError(t, resp) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckForbiddenStatus(t, resp) - - th.UpdateUserToTeamAdmin(user1, team) - th.App.InvalidateAllCaches() - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) CheckNoError(t, resp) - - // Test with SYSTEM_ADMIN level permission. - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN - }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() - - privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) - CheckNoError(t, resp) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckForbiddenStatus(t, resp) - - _, resp = th.SystemAdminClient.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckNoError(t, resp) } diff --git a/api4/context.go b/api4/context.go index b10ea7a9b..cdb9f83db 100644 --- a/api4/context.go +++ b/api4/context.go @@ -641,3 +641,26 @@ func (c *Context) RequireActionId() *Context { } return c } + +func (c *Context) RequireRoleId() *Context { + if c.Err != nil { + return c + } + + if len(c.Params.RoleId) != 26 { + c.SetInvalidUrlParam("role_id") + } + return c +} + +func (c *Context) RequireRoleName() *Context { + if c.Err != nil { + return c + } + + if !model.IsValidRoleName(c.Params.RoleName) { + c.SetInvalidUrlParam("role_name") + } + + return c +} diff --git a/api4/oauth_test.go b/api4/oauth_test.go index 8dd602456..0959442f0 100644 --- a/api4/oauth_test.go +++ b/api4/oauth_test.go @@ -19,13 +19,15 @@ func TestCreateOAuthApp(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}, IsTrusted: true} @@ -41,13 +43,15 @@ func TestCreateOAuthApp(t *testing.T) { t.Fatal("trusted did no match") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) + _, resp = Client.CreateOAuthApp(oapp) CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) + rapp, resp = Client.CreateOAuthApp(oapp) CheckNoError(t, resp) CheckCreatedStatus(t, resp) @@ -87,13 +91,15 @@ func TestUpdateOAuthApp(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{ Name: "oapp", @@ -171,8 +177,9 @@ func TestUpdateOAuthApp(t *testing.T) { th.LoginBasic() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) + _, resp = Client.UpdateOAuthApp(oapp) CheckForbiddenStatus(t, resp) @@ -181,6 +188,7 @@ func TestUpdateOAuthApp(t *testing.T) { CheckNotFoundStatus(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = false }) + _, resp = AdminClient.UpdateOAuthApp(oapp) CheckNotImplementedStatus(t, resp) @@ -200,14 +208,15 @@ func TestGetOAuthApps(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -250,8 +259,8 @@ func TestGetOAuthApps(t *testing.T) { t.Fatal("wrong apps returned") } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) _, resp = Client.GetOAuthApps(0, 1000) CheckForbiddenStatus(t, resp) @@ -273,14 +282,15 @@ func TestGetOAuthApp(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -319,8 +329,8 @@ func TestGetOAuthApp(t *testing.T) { _, resp = Client.GetOAuthApp(rapp.Id) CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) _, resp = Client.GetOAuthApp(rapp2.Id) CheckForbiddenStatus(t, resp) @@ -348,14 +358,15 @@ func TestGetOAuthAppInfo(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -394,8 +405,8 @@ func TestGetOAuthAppInfo(t *testing.T) { _, resp = Client.GetOAuthAppInfo(rapp.Id) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) _, resp = Client.GetOAuthAppInfo(rapp2.Id) CheckNoError(t, resp) @@ -423,14 +434,15 @@ func TestDeleteOAuthApp(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -464,8 +476,9 @@ func TestDeleteOAuthApp(t *testing.T) { _, resp = Client.DeleteOAuthApp(rapp2.Id) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) + _, resp = Client.DeleteOAuthApp(rapp.Id) CheckForbiddenStatus(t, resp) @@ -491,14 +504,15 @@ func TestRegenerateOAuthAppSecret(t *testing.T) { AdminClient := th.SystemAdminClient enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider - adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + + // Grant permission to regular users. + th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -536,8 +550,9 @@ func TestRegenerateOAuthAppSecret(t *testing.T) { _, resp = Client.RegenerateOAuthAppSecret(rapp2.Id) CheckNoError(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) - th.App.SetDefaultRolesBasedOnConfig() + // Revoke permission from regular users. + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID) + _, resp = Client.RegenerateOAuthAppSecret(rapp.Id) CheckForbiddenStatus(t, resp) @@ -627,7 +642,6 @@ func TestAuthorizeOAuthApp(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) - th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} diff --git a/api4/params.go b/api4/params.go index 30638578b..357036e80 100644 --- a/api4/params.go +++ b/api4/params.go @@ -44,6 +44,8 @@ type ApiParams struct { JobId string JobType string ActionId string + RoleId string + RoleName string Page int PerPage int LogsPerPage int @@ -151,6 +153,14 @@ func ApiParamsFromRequest(r *http.Request) *ApiParams { params.ActionId = val } + if val, ok := props["role_id"]; ok { + params.RoleId = val + } + + if val, ok := props["role_name"]; ok { + params.RoleName = val + } + if val, err := strconv.Atoi(r.URL.Query().Get("page")); err != nil || val < 0 { params.Page = PAGE_DEFAULT } else { diff --git a/api4/post_test.go b/api4/post_test.go index 6f770b70a..de627d8bc 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -136,14 +136,12 @@ func testCreatePostWithOutgoingHook( defer func() { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }) - th.App.SetDefaultRolesBasedOnConfig() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections }) }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) - th.App.SetDefaultRolesBasedOnConfig() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1" }) @@ -496,14 +494,11 @@ func TestUpdatePost(t *testing.T) { utils.SetIsLicensed(isLicensed) utils.SetLicense(license) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = allowEditPost }) - th.App.SetDefaultRolesBasedOnConfig() }() utils.SetIsLicensed(true) utils.SetLicense(&model.License{Features: &model.Features{}}) utils.License().Features.SetDefaults() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS }) - th.App.SetDefaultRolesBasedOnConfig() post := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a"} rpost, resp := Client.CreatePost(post) @@ -581,14 +576,12 @@ func TestPatchPost(t *testing.T) { utils.SetIsLicensed(isLicensed) utils.SetLicense(license) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = allowEditPost }) - th.App.SetDefaultRolesBasedOnConfig() }() utils.SetIsLicensed(true) utils.SetLicense(&model.License{Features: &model.Features{}}) utils.License().Features.SetDefaults() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS }) - th.App.SetDefaultRolesBasedOnConfig() post := &model.Post{ ChannelId: channel.Id, diff --git a/api4/role.go b/api4/role.go new file mode 100644 index 000000000..a401a8034 --- /dev/null +++ b/api4/role.go @@ -0,0 +1,100 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "net/http" + + "github.com/mattermost/mattermost-server/model" +) + +func (api *API) InitRole() { + api.BaseRoutes.Roles.Handle("/{role_id:[A-Za-z0-9]+}", api.ApiSessionRequiredTrustRequester(getRole)).Methods("GET") + api.BaseRoutes.Roles.Handle("/name/{role_name:[a-z0-9_]+}", api.ApiSessionRequiredTrustRequester(getRoleByName)).Methods("GET") + api.BaseRoutes.Roles.Handle("/names", api.ApiSessionRequiredTrustRequester(getRolesByNames)).Methods("POST") + api.BaseRoutes.Roles.Handle("/{role_id:[A-Za-z0-9]+}/patch", api.ApiSessionRequired(patchRole)).Methods("PUT") +} + +func getRole(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleId() + if c.Err != nil { + return + } + + if role, err := c.App.GetRole(c.Params.RoleId); err != nil { + c.Err = err + return + } else { + w.Write([]byte(role.ToJson())) + } +} + +func getRoleByName(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleName() + if c.Err != nil { + return + } + + if role, err := c.App.GetRoleByName(c.Params.RoleName); err != nil { + c.Err = err + return + } else { + w.Write([]byte(role.ToJson())) + } +} + +func getRolesByNames(c *Context, w http.ResponseWriter, r *http.Request) { + rolenames := model.ArrayFromJson(r.Body) + + if len(rolenames) == 0 { + c.SetInvalidParam("rolenames") + return + } + + for _, rolename := range rolenames { + if !model.IsValidRoleName(rolename) { + c.SetInvalidParam("rolename") + return + } + } + + if roles, err := c.App.GetRolesByNames(rolenames); err != nil { + c.Err = err + return + } else { + w.Write([]byte(model.RoleListToJson(roles))) + } +} + +func patchRole(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleId() + if c.Err != nil { + return + } + + patch := model.RolePatchFromJson(r.Body) + if patch == nil { + c.SetInvalidParam("role") + return + } + + oldRole, err := c.App.GetRole(c.Params.RoleId) + if err != nil { + c.Err = err + return + } + + if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + if role, err := c.App.PatchRole(oldRole, patch); err != nil { + c.Err = err + return + } else { + c.LogAudit("") + w.Write([]byte(role.ToJson())) + } +} diff --git a/api4/role_test.go b/api4/role_test.go new file mode 100644 index 000000000..64b8303e2 --- /dev/null +++ b/api4/role_test.go @@ -0,0 +1,184 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" +) + +func TestGetRole(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role) + assert.Nil(t, res1.Err) + role = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role.Id) + + received, resp := th.Client.GetRole(role.Id) + CheckNoError(t, resp) + + assert.Equal(t, received.Id, role.Id) + assert.Equal(t, received.Name, role.Name) + assert.Equal(t, received.DisplayName, role.DisplayName) + assert.Equal(t, received.Description, role.Description) + assert.EqualValues(t, received.Permissions, role.Permissions) + assert.Equal(t, received.SchemeManaged, role.SchemeManaged) + + _, resp = th.SystemAdminClient.GetRole("1234") + CheckBadRequestStatus(t, resp) + + _, resp = th.SystemAdminClient.GetRole(model.NewId()) + CheckNotFoundStatus(t, resp) +} + +func TestGetRoleByName(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role) + assert.Nil(t, res1.Err) + role = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role.Id) + + received, resp := th.Client.GetRoleByName(role.Name) + CheckNoError(t, resp) + + assert.Equal(t, received.Id, role.Id) + assert.Equal(t, received.Name, role.Name) + assert.Equal(t, received.DisplayName, role.DisplayName) + assert.Equal(t, received.Description, role.Description) + assert.EqualValues(t, received.Permissions, role.Permissions) + assert.Equal(t, received.SchemeManaged, role.SchemeManaged) + + _, resp = th.SystemAdminClient.GetRoleByName(strings.Repeat("abcdefghij", 10)) + CheckBadRequestStatus(t, resp) + + _, resp = th.SystemAdminClient.GetRoleByName(model.NewId()) + CheckNotFoundStatus(t, resp) +} + +func TestGetRolesByNames(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + role2 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "delete_private_channel"}, + SchemeManaged: true, + } + role3 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "manage_public_channel_properties"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role1) + assert.Nil(t, res1.Err) + role1 = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role1.Id) + + res2 := <-th.App.Srv.Store.Role().Save(role2) + assert.Nil(t, res2.Err) + role2 = res2.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role2.Id) + + res3 := <-th.App.Srv.Store.Role().Save(role3) + assert.Nil(t, res3.Err) + role3 = res3.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role3.Id) + + // Check all three roles can be found. + received, resp := th.Client.GetRolesByNames([]string{role1.Name, role2.Name, role3.Name}) + CheckNoError(t, resp) + + assert.Contains(t, received, role1) + assert.Contains(t, received, role2) + assert.Contains(t, received, role3) + + // Check a list of invalid roles. + // TODO: Confirm whether no error for invalid role names is intended. + received, resp = th.Client.GetRolesByNames([]string{model.NewId(), model.NewId()}) + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetRolesByNames([]string{}) + CheckBadRequestStatus(t, resp) +} + +func TestPatchRole(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role) + assert.Nil(t, res1.Err) + role = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role.Id) + + patch := &model.RolePatch{ + Permissions: &[]string{"manage_system", "delete_public_channel"}, + } + + received, resp := th.SystemAdminClient.PatchRole(role.Id, patch) + CheckNoError(t, resp) + + assert.Equal(t, received.Id, role.Id) + assert.Equal(t, received.Name, role.Name) + assert.Equal(t, received.DisplayName, role.DisplayName) + assert.Equal(t, received.Description, role.Description) + assert.EqualValues(t, received.Permissions, []string{"manage_system", "delete_public_channel"}) + assert.Equal(t, received.SchemeManaged, role.SchemeManaged) + + // Check a no-op patch succeeds. + received, resp = th.SystemAdminClient.PatchRole(role.Id, patch) + CheckNoError(t, resp) + + received, resp = th.SystemAdminClient.PatchRole("junk", patch) + CheckBadRequestStatus(t, resp) + + received, resp = th.Client.PatchRole(model.NewId(), patch) + CheckNotFoundStatus(t, resp) + + received, resp = th.Client.PatchRole(role.Id, patch) + CheckForbiddenStatus(t, resp) +} diff --git a/api4/team_test.go b/api4/team_test.go index a8696a30b..272b7372e 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -67,14 +67,14 @@ func TestCreateTeam(t *testing.T) { _, resp = Client.CreateTeam(rteam) CheckUnauthorizedStatus(t, resp) - // Update permission - enableTeamCreation := th.App.Config().TeamSettings.EnableTeamCreation + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableTeamCreation = enableTeamCreation }) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() - th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableTeamCreation = false }) - th.App.SetDefaultRolesBasedOnConfig() + + th.RemovePermissionFromRole(model.PERMISSION_CREATE_TEAM.Id, model.SYSTEM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_CREATE_TEAM.Id, model.SYSTEM_ADMIN_ROLE_ID) th.LoginBasic() _, resp = Client.CreateTeam(team) @@ -1292,31 +1292,18 @@ func TestAddTeamMember(t *testing.T) { Client.Logout() - // Check effects of config and license changes. - restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite - isLicensed := utils.IsLicensed() - license := utils.License() + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() // Set the config so that only team admins can add a user to a team. - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - th.LoginBasic() - - // Test without the EE license to see that the permission restriction is ignored. - _, resp = Client.AddTeamMember(team.Id, otherUser.Id) - CheckNoError(t, resp) + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) - // Add an EE license. - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Check that a regular user can't add someone to the team. @@ -1326,50 +1313,26 @@ func TestAddTeamMember(t *testing.T) { // Update user to team admin th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a team admin. _, resp = Client.AddTeamMember(team.Id, otherUser.Id) CheckNoError(t, resp) - // Change permission level to System Admin - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() + // Change permission level to team user + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) - // Should not work as team admin. - _, resp = Client.AddTeamMember(team.Id, otherUser.Id) - CheckForbiddenStatus(t, resp) - - // Should work as system admin. - _, resp = th.SystemAdminClient.AddTeamMember(team.Id, otherUser.Id) - CheckNoError(t, resp) - - // Change permission level to All th.UpdateUserToNonTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a regular user. _, resp = Client.AddTeamMember(team.Id, otherUser.Id) CheckNoError(t, resp) - // Reset config and license. - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() - th.LoginBasic() - // by hash and data Client.Login(otherUser.Email, otherUser.Password) @@ -1506,31 +1469,18 @@ func TestAddTeamMembers(t *testing.T) { Client.Logout() - // Check effects of config and license changes. - restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite - isLicensed := utils.IsLicensed() - license := utils.License() + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) - utils.SetIsLicensed(isLicensed) - utils.SetLicense(license) - th.App.SetDefaultRolesBasedOnConfig() + th.RestoreDefaultRolePermissions(defaultRolePermissions) }() // Set the config so that only team admins can add a user to a team. - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - th.LoginBasic() - - // Test without the EE license to see that the permission restriction is ignored. - _, resp = Client.AddTeamMembers(team.Id, userList) - CheckNoError(t, resp) + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) - // Add an EE license. - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Check that a regular user can't add someone to the team. @@ -1540,37 +1490,20 @@ func TestAddTeamMembers(t *testing.T) { // Update user to team admin th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a team admin. _, resp = Client.AddTeamMembers(team.Id, userList) CheckNoError(t, resp) - // Change permission level to System Admin - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) - th.App.SetDefaultRolesBasedOnConfig() - - // Should not work as team admin. - _, resp = Client.AddTeamMembers(team.Id, userList) - CheckForbiddenStatus(t, resp) - - // Should work as system admin. - _, resp = th.SystemAdminClient.AddTeamMembers(team.Id, userList) - CheckNoError(t, resp) + // Change permission level to team user + th.AddPermissionToRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_INVITE_USER.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_ADD_USER_TO_TEAM.Id, model.TEAM_ADMIN_ROLE_ID) - // Change permission level to All th.UpdateUserToNonTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_ALL }) - utils.SetIsLicensed(true) - utils.SetLicense(&model.License{Features: &model.Features{}}) - utils.License().Features.SetDefaults() - th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a regular user. diff --git a/api4/webhook_test.go b/api4/webhook_test.go index 724fd0ea4..0a295b4b2 100644 --- a/api4/webhook_test.go +++ b/api4/webhook_test.go @@ -19,10 +19,16 @@ func TestCreateIncomingWebhook(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostIconOverride = true }) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id} rhook, resp := th.SystemAdminClient.CreateIncomingWebhook(hook) @@ -53,7 +59,7 @@ func TestCreateIncomingWebhook(t *testing.T) { _, resp = Client.CreateIncomingWebhook(hook) CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) _, resp = Client.CreateIncomingWebhook(hook) CheckNoError(t, resp) @@ -75,7 +81,13 @@ func TestGetIncomingWebhooks(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id} rhook, resp := th.SystemAdminClient.CreateIncomingWebhook(hook) @@ -126,7 +138,7 @@ func TestGetIncomingWebhooks(t *testing.T) { _, resp = Client.GetIncomingWebhooks(0, 1000, "") CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) _, resp = Client.GetIncomingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") CheckNoError(t, resp) @@ -148,7 +160,6 @@ func TestGetIncomingWebhook(t *testing.T) { Client := th.SystemAdminClient th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) var resp *model.Response var rhook *model.IncomingWebhook @@ -188,7 +199,6 @@ func TestDeleteIncomingWebhook(t *testing.T) { Client := th.SystemAdminClient th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) var resp *model.Response var rhook *model.IncomingWebhook @@ -240,7 +250,13 @@ func TestCreateOutgoingWebhook(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} @@ -268,7 +284,7 @@ func TestCreateOutgoingWebhook(t *testing.T) { _, resp = Client.CreateOutgoingWebhook(hook) CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) _, resp = Client.CreateOutgoingWebhook(hook) CheckNoError(t, resp) @@ -284,7 +300,12 @@ func TestGetOutgoingWebhooks(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} rhook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) @@ -352,7 +373,7 @@ func TestGetOutgoingWebhooks(t *testing.T) { _, resp = Client.GetOutgoingWebhooks(0, 1000, "") CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) _, resp = Client.GetOutgoingWebhooksForTeam(th.BasicTeam.Id, 0, 1000, "") CheckNoError(t, resp) @@ -380,7 +401,6 @@ func TestGetOutgoingWebhook(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} @@ -411,7 +431,13 @@ func TestUpdateIncomingHook(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook1 := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id} @@ -547,10 +573,11 @@ func TestUpdateIncomingHook(t *testing.T) { CheckForbiddenStatus(t, resp) }) - th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) t.Run("OnlyAdminIntegrationsDisabled", func(t *testing.T) { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) t.Run("UpdateHookOfSameUser", func(t *testing.T) { sameUserHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id} @@ -568,7 +595,8 @@ func TestUpdateIncomingHook(t *testing.T) { }) }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) Client.Logout() th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) @@ -623,7 +651,6 @@ func TestRegenOutgoingHookToken(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}} rhook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) @@ -656,7 +683,12 @@ func TestUpdateOutgoingHook(t *testing.T) { Client := th.Client th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) createdHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}, TriggerWords: []string{"cats"}} @@ -729,7 +761,7 @@ func TestUpdateOutgoingHook(t *testing.T) { CheckForbiddenStatus(t, resp) }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) hook2 := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}, TriggerWords: []string{"rats2"}} @@ -739,7 +771,8 @@ func TestUpdateOutgoingHook(t *testing.T) { _, resp = Client.UpdateOutgoingWebhook(createdHook2) CheckForbiddenStatus(t, resp) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) Client.Logout() th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) @@ -813,7 +846,6 @@ func TestDeleteOutgoingHook(t *testing.T) { Client := th.SystemAdminClient th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) var resp *model.Response var rhook *model.OutgoingWebhook diff --git a/app/app.go b/app/app.go index 1e46d29d0..4cc9ff7df 100644 --- a/app/app.go +++ b/app/app.go @@ -7,6 +7,7 @@ import ( "html/template" "net" "net/http" + "reflect" "strings" "sync" "sync/atomic" @@ -25,6 +26,8 @@ import ( "github.com/mattermost/mattermost-server/utils" ) +const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete" + type App struct { goroutineCount int32 goroutineExitSignal chan struct{} @@ -62,7 +65,6 @@ type App struct { htmlTemplateWatcher *utils.HTMLTemplateWatcher sessionCache *utils.Cache - roles map[string]*model.Role configListenerId string licenseListenerId string disableConfigWatch bool @@ -120,7 +122,6 @@ func New(options ...Option) (*App, error) { }) app.licenseListenerId = utils.AddLicenseListener(app.configOrLicenseListener) app.regenerateClientConfig() - app.SetDefaultRolesBasedOnConfig() l4g.Info(utils.T("api.server.new_server.init.info")) @@ -157,7 +158,6 @@ func New(options ...Option) (*App, error) { func (a *App) configOrLicenseListener() { a.regenerateClientConfig() - a.SetDefaultRolesBasedOnConfig() } func (a *App) Shutdown() { @@ -450,3 +450,57 @@ func (a *App) Handle404(w http.ResponseWriter, r *http.Request) { utils.RenderWebError(err, w, r) } + +// This function migrates the default built in roles from code/config to the database. +func (a *App) DoAdvancedPermissionsMigration() { + // If the migration is already marked as completed, don't do it again. + if result := <-a.Srv.Store.System().GetByName(ADVANCED_PERMISSIONS_MIGRATION_KEY); result.Err == nil { + return + } + + l4g.Info("Migrating roles to database.") + roles := model.MakeDefaultRoles() + roles = utils.SetRolePermissionsFromConfig(roles, a.Config()) + + allSucceeded := true + + for _, role := range roles { + if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { + // If this failed for reasons other than the role already existing, don't mark the migration as done. + if result2 := <-a.Srv.Store.Role().GetByName(role.Name); result2.Err != nil { + l4g.Critical("Failed to migrate role to database.") + l4g.Critical(result.Err) + allSucceeded = false + } else { + // If the role already existed, check it is the same and update if not. + fetchedRole := result.Data.(*model.Role) + if !reflect.DeepEqual(fetchedRole.Permissions, role.Permissions) || + fetchedRole.DisplayName != role.DisplayName || + fetchedRole.Description != role.Description || + fetchedRole.SchemeManaged != role.SchemeManaged { + role.Id = fetchedRole.Id + if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { + // Role is not the same, but failed to update. + l4g.Critical("Failed to migrate role to database.") + l4g.Critical(result.Err) + allSucceeded = false + } + } + } + } + } + + if !allSucceeded { + return + } + + system := model.System{ + Name: ADVANCED_PERMISSIONS_MIGRATION_KEY, + Value: "true", + } + + if result := <-a.Srv.Store.System().Save(&system); result.Err != nil { + l4g.Critical("Failed to mark advanced permissions migration as completed.") + l4g.Critical(result.Err) + } +} diff --git a/app/app_test.go b/app/app_test.go index 25b19ead8..6d62bf249 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -9,7 +9,6 @@ import ( "testing" l4g "github.com/alecthomas/log4go" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -71,3 +70,342 @@ func TestUpdateConfig(t *testing.T) { *cfg.ServiceSettings.SiteURL = "foo" }) } + +func TestDoAdvancedPermissionsMigration(t *testing.T) { + th := Setup() + defer th.TearDown() + + if testStoreSqlSupplier == nil { + t.Skip("This test requires a TestStore to be run.") + } + + th.ResetRoleMigration() + + th.App.DoAdvancedPermissionsMigration() + + roleNames := []string{ + "system_user", + "system_admin", + "team_user", + "team_admin", + "channel_user", + "channel_admin", + "system_post_all", + "system_post_all_public", + "system_user_access_token", + "team_post_all", + "team_post_all_public", + } + + roles1, err1 := th.App.GetRolesByNames(roleNames) + assert.Nil(t, err1) + assert.Equal(t, len(roles1), len(roleNames)) + + expected1 := map[string][]string{ + "channel_user": []string{ + model.PERMISSION_READ_CHANNEL.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + model.PERMISSION_UPLOAD_FILE.Id, + model.PERMISSION_GET_PUBLIC_LINK.Id, + model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_EDIT_POST.Id, + model.PERMISSION_USE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, + model.PERMISSION_DELETE_POST.Id, + }, + "channel_admin": []string{ + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + }, + "team_user": []string{ + model.PERMISSION_LIST_TEAM_CHANNELS.Id, + model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + model.PERMISSION_READ_PUBLIC_CHANNEL.Id, + model.PERMISSION_VIEW_TEAM.Id, + model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, + model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, + model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, + model.PERMISSION_INVITE_USER.Id, + model.PERMISSION_ADD_USER_TO_TEAM.Id, + }, + "team_post_all": []string{ + model.PERMISSION_CREATE_POST.Id, + }, + "team_post_all_public": []string{ + model.PERMISSION_CREATE_POST_PUBLIC.Id, + }, + "team_admin": []string{ + model.PERMISSION_EDIT_OTHERS_POSTS.Id, + model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, + model.PERMISSION_MANAGE_TEAM.Id, + model.PERMISSION_IMPORT_TEAM.Id, + model.PERMISSION_MANAGE_TEAM_ROLES.Id, + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_WEBHOOKS.Id, + model.PERMISSION_DELETE_POST.Id, + model.PERMISSION_DELETE_OTHERS_POSTS.Id, + }, + "system_user": []string{ + model.PERMISSION_CREATE_DIRECT_CHANNEL.Id, + model.PERMISSION_CREATE_GROUP_CHANNEL.Id, + model.PERMISSION_PERMANENT_DELETE_USER.Id, + model.PERMISSION_CREATE_TEAM.Id, + }, + "system_post_all": []string{ + model.PERMISSION_CREATE_POST.Id, + }, + "system_post_all_public": []string{ + model.PERMISSION_CREATE_POST_PUBLIC.Id, + }, + "system_user_access_token": []string{ + model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_READ_USER_ACCESS_TOKEN.Id, + model.PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + }, + "system_admin": []string{ + model.PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, + model.PERMISSION_MANAGE_SYSTEM.Id, + model.PERMISSION_MANAGE_ROLES.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, + model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, + model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, + model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, + model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, + model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id, + model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + model.PERMISSION_EDIT_OTHER_USERS.Id, + model.PERMISSION_MANAGE_OAUTH.Id, + model.PERMISSION_INVITE_USER.Id, + model.PERMISSION_DELETE_POST.Id, + model.PERMISSION_DELETE_OTHERS_POSTS.Id, + model.PERMISSION_CREATE_TEAM.Id, + model.PERMISSION_ADD_USER_TO_TEAM.Id, + model.PERMISSION_LIST_USERS_WITHOUT_TEAM.Id, + model.PERMISSION_MANAGE_JOBS.Id, + model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_READ_USER_ACCESS_TOKEN.Id, + model.PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_LIST_TEAM_CHANNELS.Id, + model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + model.PERMISSION_READ_PUBLIC_CHANNEL.Id, + model.PERMISSION_VIEW_TEAM.Id, + model.PERMISSION_READ_CHANNEL.Id, + model.PERMISSION_UPLOAD_FILE.Id, + model.PERMISSION_GET_PUBLIC_LINK.Id, + model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_EDIT_POST.Id, + model.PERMISSION_USE_SLASH_COMMANDS.Id, + model.PERMISSION_EDIT_OTHERS_POSTS.Id, + model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, + model.PERMISSION_MANAGE_TEAM.Id, + model.PERMISSION_IMPORT_TEAM.Id, + model.PERMISSION_MANAGE_TEAM_ROLES.Id, + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_WEBHOOKS.Id, + }, + } + + // Check the migration matches what's expected. + for name, permissions := range expected1 { + role, err := th.App.GetRoleByName(name) + assert.Nil(t, err) + assert.Equal(t, role.Permissions, permissions) + } + + // Add a license and change the policy config. + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + }() + + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + + // Check the migration doesn't change anything if run again. + th.App.DoAdvancedPermissionsMigration() + + roles2, err2 := th.App.GetRolesByNames(roleNames) + assert.Nil(t, err2) + assert.Equal(t, len(roles2), len(roleNames)) + + for name, permissions := range expected1 { + role, err := th.App.GetRoleByName(name) + assert.Nil(t, err) + assert.Equal(t, permissions, role.Permissions) + } + + // Reset the database + th.ResetRoleMigration() + + // Do the migration again with different policy config settings and a license. + th.App.DoAdvancedPermissionsMigration() + + // Check the role permissions. + expected2 := map[string][]string{ + "channel_user": []string{ + model.PERMISSION_READ_CHANNEL.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + model.PERMISSION_UPLOAD_FILE.Id, + model.PERMISSION_GET_PUBLIC_LINK.Id, + model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_EDIT_POST.Id, + model.PERMISSION_USE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, + model.PERMISSION_DELETE_POST.Id, + }, + "channel_admin": []string{ + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + }, + "team_user": []string{ + model.PERMISSION_LIST_TEAM_CHANNELS.Id, + model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + model.PERMISSION_READ_PUBLIC_CHANNEL.Id, + model.PERMISSION_VIEW_TEAM.Id, + model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, + model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, + model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, + model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, + model.PERMISSION_INVITE_USER.Id, + model.PERMISSION_ADD_USER_TO_TEAM.Id, + }, + "team_post_all": []string{ + model.PERMISSION_CREATE_POST.Id, + }, + "team_post_all_public": []string{ + model.PERMISSION_CREATE_POST_PUBLIC.Id, + }, + "team_admin": []string{ + model.PERMISSION_EDIT_OTHERS_POSTS.Id, + model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, + model.PERMISSION_MANAGE_TEAM.Id, + model.PERMISSION_IMPORT_TEAM.Id, + model.PERMISSION_MANAGE_TEAM_ROLES.Id, + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_WEBHOOKS.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, + model.PERMISSION_DELETE_POST.Id, + model.PERMISSION_DELETE_OTHERS_POSTS.Id, + }, + "system_user": []string{ + model.PERMISSION_CREATE_DIRECT_CHANNEL.Id, + model.PERMISSION_CREATE_GROUP_CHANNEL.Id, + model.PERMISSION_PERMANENT_DELETE_USER.Id, + model.PERMISSION_CREATE_TEAM.Id, + }, + "system_post_all": []string{ + model.PERMISSION_CREATE_POST.Id, + }, + "system_post_all_public": []string{ + model.PERMISSION_CREATE_POST_PUBLIC.Id, + }, + "system_user_access_token": []string{ + model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_READ_USER_ACCESS_TOKEN.Id, + model.PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + }, + "system_admin": []string{ + model.PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, + model.PERMISSION_MANAGE_SYSTEM.Id, + model.PERMISSION_MANAGE_ROLES.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, + model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, + model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id, + model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, + model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, + model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id, + model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id, + model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + model.PERMISSION_EDIT_OTHER_USERS.Id, + model.PERMISSION_MANAGE_OAUTH.Id, + model.PERMISSION_INVITE_USER.Id, + model.PERMISSION_DELETE_POST.Id, + model.PERMISSION_DELETE_OTHERS_POSTS.Id, + model.PERMISSION_CREATE_TEAM.Id, + model.PERMISSION_ADD_USER_TO_TEAM.Id, + model.PERMISSION_LIST_USERS_WITHOUT_TEAM.Id, + model.PERMISSION_MANAGE_JOBS.Id, + model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_READ_USER_ACCESS_TOKEN.Id, + model.PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + model.PERMISSION_LIST_TEAM_CHANNELS.Id, + model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + model.PERMISSION_READ_PUBLIC_CHANNEL.Id, + model.PERMISSION_VIEW_TEAM.Id, + model.PERMISSION_READ_CHANNEL.Id, + model.PERMISSION_UPLOAD_FILE.Id, + model.PERMISSION_GET_PUBLIC_LINK.Id, + model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_EDIT_POST.Id, + model.PERMISSION_USE_SLASH_COMMANDS.Id, + model.PERMISSION_EDIT_OTHERS_POSTS.Id, + model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, + model.PERMISSION_MANAGE_TEAM.Id, + model.PERMISSION_IMPORT_TEAM.Id, + model.PERMISSION_MANAGE_TEAM_ROLES.Id, + model.PERMISSION_MANAGE_CHANNEL_ROLES.Id, + model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, + model.PERMISSION_MANAGE_WEBHOOKS.Id, + }, + } + + roles3, err3 := th.App.GetRolesByNames(roleNames) + assert.Nil(t, err3) + assert.Equal(t, len(roles3), len(roleNames)) + + for name, permissions := range expected2 { + role, err := th.App.GetRoleByName(name) + assert.Nil(t, err) + assert.Equal(t, permissions, role.Permissions) + } + + // Remove the license. + utils.SetIsLicensed(false) + + // Do the migration again. + th.ResetRoleMigration() + th.App.DoAdvancedPermissionsMigration() + + // Check the role permissions. + roles4, err4 := th.App.GetRolesByNames(roleNames) + assert.Nil(t, err4) + assert.Equal(t, len(roles4), len(roleNames)) + + for name, permissions := range expected1 { + role, err := th.App.GetRoleByName(name) + assert.Nil(t, err) + assert.Equal(t, permissions, role.Permissions) + } +} diff --git a/app/apptestlib.go b/app/apptestlib.go index 09afc8f76..2b56c7ded 100644 --- a/app/apptestlib.go +++ b/app/apptestlib.go @@ -13,6 +13,7 @@ import ( l4g "github.com/alecthomas/log4go" + "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/plugin" "github.com/mattermost/mattermost-server/plugin/pluginenv" @@ -43,12 +44,16 @@ func (*persistentTestStore) Close() {} var testStoreContainer *storetest.RunningContainer var testStore *persistentTestStore +var testStoreSqlSupplier *sqlstore.SqlSupplier +var testClusterInterface *FakeClusterInterface // UseTestStore sets the container and corresponding settings to use for tests. Once the tests are // complete (e.g. at the end of your TestMain implementation), you should call StopTestStore. func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) { + testClusterInterface = &FakeClusterInterface{} testStoreContainer = container - testStore = &persistentTestStore{store.NewLayeredStore(sqlstore.NewSqlSupplier(*settings, nil), nil, nil)} + testStoreSqlSupplier = sqlstore.NewSqlSupplier(*settings, nil) + testStore = &persistentTestStore{store.NewLayeredStore(testStoreSqlSupplier, nil, testClusterInterface)} } func StopTestStore() { @@ -98,6 +103,9 @@ func setupTestHelper(enterprise bool) *TestHelper { } th.App.StartServer() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress }) + + th.App.DoAdvancedPermissionsMigration() + th.App.Srv.Store.MarkSystemRanUnitTests() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) @@ -313,3 +321,45 @@ func (me *TestHelper) InstallPlugin(manifest *model.Manifest, hooks plugin.Hooks panic(err) } } + +func (me *TestHelper) ResetRoleMigration() { + if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil { + panic(err) + } + + testClusterInterface.sendClearRoleCacheMessage() + + if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil { + panic(err) + } +} + +type FakeClusterInterface struct { + clusterMessageHandler einterfaces.ClusterMessageHandler +} + +func (me *FakeClusterInterface) StartInterNodeCommunication() {} +func (me *FakeClusterInterface) StopInterNodeCommunication() {} +func (me *FakeClusterInterface) RegisterClusterMessageHandler(event string, crm einterfaces.ClusterMessageHandler) { + me.clusterMessageHandler = crm +} +func (me *FakeClusterInterface) GetClusterId() string { return "" } +func (me *FakeClusterInterface) IsLeader() bool { return false } +func (me *FakeClusterInterface) GetMyClusterInfo() *model.ClusterInfo { return nil } +func (me *FakeClusterInterface) GetClusterInfos() []*model.ClusterInfo { return nil } +func (me *FakeClusterInterface) SendClusterMessage(cluster *model.ClusterMessage) {} +func (me *FakeClusterInterface) NotifyMsg(buf []byte) {} +func (me *FakeClusterInterface) GetClusterStats() ([]*model.ClusterStats, *model.AppError) { + return nil, nil +} +func (me *FakeClusterInterface) GetLogs(page, perPage int) ([]string, *model.AppError) { + return []string{}, nil +} +func (me *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError { + return nil +} +func (me *FakeClusterInterface) sendClearRoleCacheMessage() { + me.clusterMessageHandler(&model.ClusterMessage{ + Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, + }) +} diff --git a/app/authorization.go b/app/authorization.go index 3a64bb717..632dd7566 100644 --- a/app/authorization.go +++ b/app/authorization.go @@ -12,7 +12,7 @@ import ( ) func (a *App) SessionHasPermissionTo(session model.Session, permission *model.Permission) bool { - if !a.CheckIfRolesGrantPermission(session.GetUserRoles(), permission.Id) { + if !a.RolesGrantPermission(session.GetUserRoles(), permission.Id) { a.ClearSessionCacheForUser(session.UserId) return false } @@ -28,12 +28,12 @@ func (a *App) SessionHasPermissionToTeam(session model.Session, teamId string, p teamMember := session.GetTeamByTeamId(teamId) if teamMember != nil { - if a.CheckIfRolesGrantPermission(teamMember.GetRoles(), permission.Id) { + if a.RolesGrantPermission(teamMember.GetRoles(), permission.Id) { return true } } - return a.CheckIfRolesGrantPermission(session.GetUserRoles(), permission.Id) + return a.RolesGrantPermission(session.GetUserRoles(), permission.Id) } func (a *App) SessionHasPermissionToChannel(session model.Session, channelId string, permission *model.Permission) bool { @@ -48,7 +48,7 @@ func (a *App) SessionHasPermissionToChannel(session model.Session, channelId str ids := cmcresult.Data.(map[string]string) if roles, ok := ids[channelId]; ok { channelRoles = strings.Fields(roles) - if a.CheckIfRolesGrantPermission(channelRoles, permission.Id) { + if a.RolesGrantPermission(channelRoles, permission.Id) { return true } } @@ -69,7 +69,7 @@ func (a *App) SessionHasPermissionToChannelByPost(session model.Session, postId if result := <-a.Srv.Store.Channel().GetMemberForPost(postId, session.UserId); result.Err == nil { channelMember = result.Data.(*model.ChannelMember) - if a.CheckIfRolesGrantPermission(channelMember.GetRoles(), permission.Id) { + if a.RolesGrantPermission(channelMember.GetRoles(), permission.Id) { return true } } @@ -119,7 +119,7 @@ func (a *App) HasPermissionTo(askingUserId string, permission *model.Permission) roles := user.GetRoles() - return a.CheckIfRolesGrantPermission(roles, permission.Id) + return a.RolesGrantPermission(roles, permission.Id) } func (a *App) HasPermissionToTeam(askingUserId string, teamId string, permission *model.Permission) bool { @@ -134,7 +134,7 @@ func (a *App) HasPermissionToTeam(askingUserId string, teamId string, permission roles := teamMember.GetRoles() - if a.CheckIfRolesGrantPermission(roles, permission.Id) { + if a.RolesGrantPermission(roles, permission.Id) { return true } @@ -149,7 +149,7 @@ func (a *App) HasPermissionToChannel(askingUserId string, channelId string, perm channelMember, err := a.GetChannelMember(channelId, askingUserId) if err == nil { roles := channelMember.GetRoles() - if a.CheckIfRolesGrantPermission(roles, permission.Id) { + if a.RolesGrantPermission(roles, permission.Id) { return true } } @@ -168,7 +168,7 @@ func (a *App) HasPermissionToChannelByPost(askingUserId string, postId string, p if result := <-a.Srv.Store.Channel().GetMemberForPost(postId, askingUserId); result.Err == nil { channelMember = result.Data.(*model.ChannelMember) - if a.CheckIfRolesGrantPermission(channelMember.GetRoles(), permission.Id) { + if a.RolesGrantPermission(channelMember.GetRoles(), permission.Id) { return true } } @@ -193,17 +193,21 @@ func (a *App) HasPermissionToUser(askingUserId string, userId string) bool { return false } -func (a *App) CheckIfRolesGrantPermission(roles []string, permissionId string) bool { - for _, roleId := range roles { - if role := a.Role(roleId); role == nil { - l4g.Debug("Bad role in system " + roleId) - return false - } else { - permissions := role.Permissions - for _, permission := range permissions { - if permission == permissionId { - return true - } +func (a *App) RolesGrantPermission(roleNames []string, permissionId string) bool { + roles, err := a.GetRolesByNames(roleNames) + if err != nil { + // This should only happen if something is very broken. We can't realistically + // recover the situation, so deny permission and log an error. + l4g.Error("Failed to get roles from database with role names: " + strings.Join(roleNames, ",")) + l4g.Error(err) + return false + } + + for _, role := range roles { + permissions := role.Permissions + for _, permission := range permissions { + if permission == permissionId { + return true } } } diff --git a/app/authorization_test.go b/app/authorization_test.go index a65fe8333..2127a682e 100644 --- a/app/authorization_test.go +++ b/app/authorization_test.go @@ -18,9 +18,9 @@ func TestCheckIfRolesGrantPermission(t *testing.T) { permissionId string shouldGrant bool }{ - {[]string{model.SYSTEM_ADMIN_ROLE_ID}, th.App.Role(model.SYSTEM_ADMIN_ROLE_ID).Permissions[0], true}, + {[]string{model.SYSTEM_ADMIN_ROLE_ID}, model.PERMISSION_MANAGE_SYSTEM.Id, true}, {[]string{model.SYSTEM_ADMIN_ROLE_ID}, "non-existant-permission", false}, - {[]string{model.CHANNEL_USER_ROLE_ID}, th.App.Role(model.CHANNEL_USER_ROLE_ID).Permissions[0], true}, + {[]string{model.CHANNEL_USER_ROLE_ID}, model.PERMISSION_READ_CHANNEL.Id, true}, {[]string{model.CHANNEL_USER_ROLE_ID}, model.PERMISSION_MANAGE_SYSTEM.Id, false}, {[]string{model.SYSTEM_ADMIN_ROLE_ID, model.CHANNEL_USER_ROLE_ID}, model.PERMISSION_MANAGE_SYSTEM.Id, true}, {[]string{model.CHANNEL_USER_ROLE_ID, model.SYSTEM_ADMIN_ROLE_ID}, model.PERMISSION_MANAGE_SYSTEM.Id, true}, @@ -29,7 +29,7 @@ func TestCheckIfRolesGrantPermission(t *testing.T) { } for testnum, testcase := range cases { - if th.App.CheckIfRolesGrantPermission(testcase.roles, testcase.permissionId) != testcase.shouldGrant { + if th.App.RolesGrantPermission(testcase.roles, testcase.permissionId) != testcase.shouldGrant { t.Fatal("Failed test case ", testnum) } } diff --git a/app/channel.go b/app/channel.go index 0054fe14b..c588ebda9 100644 --- a/app/channel.go +++ b/app/channel.go @@ -431,6 +431,10 @@ func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles return nil, err } + if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil { + return nil, err + } + member.Roles = newRoles if result := <-a.Srv.Store.Channel().UpdateMember(member); result.Err != nil { diff --git a/app/import_test.go b/app/import_test.go index 6a284f63d..be2befd82 100644 --- a/app/import_test.go +++ b/app/import_test.go @@ -411,10 +411,6 @@ func TestImportValidateUserImportData(t *testing.T) { } data.Position = ptrStr("The Boss") - data.Roles = ptrStr("system_user wat") - if err := validateUserImportData(&data); err == nil { - t.Fatal("Validation should have failed due to too unrecognised role.") - } data.Roles = nil if err := validateUserImportData(&data); err != nil { t.Fatal("Validation failed but should have been valid.") @@ -478,12 +474,6 @@ func TestImportValidateUserTeamsImportData(t *testing.T) { } data[0].Name = ptrStr("teamname") - // Invalid Roles - data[0].Roles = ptrStr("wtf") - if err := validateUserTeamsImportData(&data); err == nil { - t.Fatal("Should have failed due to invalid roles.") - } - // Valid (nil roles) data[0].Roles = nil if err := validateUserTeamsImportData(&data); err != nil { @@ -516,12 +506,6 @@ func TestImportValidateUserChannelsImportData(t *testing.T) { } data[0].Name = ptrStr("channelname") - // Invalid Roles - data[0].Roles = ptrStr("wtf") - if err := validateUserChannelsImportData(&data); err == nil { - t.Fatal("Should have failed due to invalid roles.") - } - // Valid (nil roles) data[0].Roles = nil if err := validateUserChannelsImportData(&data); err != nil { diff --git a/app/post.go b/app/post.go index bf4725e77..fc42a6b6b 100644 --- a/app/post.go +++ b/app/post.go @@ -127,7 +127,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo if utils.IsLicensed() && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && !post.IsSystemMessage() && channel.Name == model.DEFAULT_CHANNEL && - !a.CheckIfRolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { + !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { return nil, model.NewAppError("createPost", "api.post.create_post.town_square_read_only", nil, "", http.StatusForbidden) } diff --git a/app/role.go b/app/role.go index 5f39dd623..c99d8365b 100644 --- a/app/role.go +++ b/app/role.go @@ -1,19 +1,91 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package app import ( + "reflect" + "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/utils" + "net/http" ) -func (a *App) Role(id string) *model.Role { - return a.roles[id] +func (a *App) GetRole(id string) (*model.Role, *model.AppError) { + if result := <-a.Srv.Store.Role().Get(id); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.Role), nil + } +} + +func (a *App) GetRoleByName(name string) (*model.Role, *model.AppError) { + if result := <-a.Srv.Store.Role().GetByName(name); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.Role), nil + } +} + +func (a *App) GetRolesByNames(names []string) ([]*model.Role, *model.AppError) { + if result := <-a.Srv.Store.Role().GetByNames(names); result.Err != nil { + return nil, result.Err + } else { + return result.Data.([]*model.Role), nil + } +} + +func (a *App) PatchRole(role *model.Role, patch *model.RolePatch) (*model.Role, *model.AppError) { + // If patch is a no-op then short-circuit the store. + if patch.Permissions != nil && reflect.DeepEqual(*patch.Permissions, role.Permissions) { + return role, nil + } + + role.Patch(patch) + role, err := a.UpdateRole(role) + if err != nil { + return nil, err + } + + return role, err } -// Updates the roles based on the app config and the global license check. You may need to invoke -// this when license changes are made. -func (a *App) SetDefaultRolesBasedOnConfig() { - a.roles = utils.DefaultRolesBasedOnConfig(a.Config()) +func (a *App) UpdateRole(role *model.Role) (*model.Role, *model.AppError) { + if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { + return nil, result.Err + } else { + a.sendUpdatedRoleEvent(role) + + return role, nil + } +} + +func (a *App) CheckRolesExist(roleNames []string) *model.AppError { + roles, err := a.GetRolesByNames(roleNames) + if err != nil { + return err + } + + for _, name := range roleNames { + nameFound := false + for _, role := range roles { + if name == role.Name { + nameFound = true + break + } + } + if !nameFound { + return model.NewAppError("CheckRolesExist", "app.role.check_roles_exist.role_not_found", nil, "role="+name, http.StatusBadRequest) + } + } + + return nil +} + +func (a *App) sendUpdatedRoleEvent(role *model.Role) { + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_ROLE_UPDATED, "", "", "", nil) + message.Add("role", role.ToJson()) + + a.Go(func() { + a.Publish(message) + }) } diff --git a/app/team.go b/app/team.go index 71eb00569..95d9895ec 100644 --- a/app/team.go +++ b/app/team.go @@ -157,6 +157,10 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin return nil, err } + if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil { + return nil, err + } + member.Roles = newRoles if result := <-a.Srv.Store.Team().UpdateMember(member); result.Err != nil { diff --git a/app/user.go b/app/user.go index 64e49e293..156503fb0 100644 --- a/app/user.go +++ b/app/user.go @@ -1230,6 +1230,10 @@ func (a *App) UpdateUserRoles(userId string, newRoles string, sendWebSocketEvent return nil, err } + if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil { + return nil, err + } + user.Roles = newRoles uchan := a.Srv.Store.User().Update(user, true) schan := a.Srv.Store.Session().UpdateRoles(user.Id, newRoles) diff --git a/cmd/platform/server.go b/cmd/platform/server.go index d289898da..cfcefbba8 100644 --- a/cmd/platform/server.go +++ b/cmd/platform/server.go @@ -78,6 +78,8 @@ func runServer(configFileLocation string, disableConfigWatch bool) { a.LoadLicense() } + a.DoAdvancedPermissionsMigration() + a.InitPlugins(*a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil) a.AddConfigListener(func(prevCfg, cfg *model.Config) { if *cfg.PluginSettings.Enable { diff --git a/i18n/en.json b/i18n/en.json index 88df06f37..606b61181 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2342,10 +2342,18 @@ "id": "api.templates.email_change_body.info", "translation": "Your email address for {{.TeamDisplayName}} has been changed to {{.NewEmail}}.
If you did not make this change, please contact the system administrator." }, + { + "id": "app.role.check_roles_exist.role_not_found", + "translation": "The provided role does not exist" + }, { "id": "api.templates.email_change_body.title", "translation": "You updated your email" }, + { + "id": "store.sql_role.save.invalid_role.app_error", + "translation": "The role was not valid" + }, { "id": "api.templates.email_change_subject", "translation": "[{{ .SiteName }}] Your email address has changed" @@ -6422,6 +6430,26 @@ "id": "store.sql_reaction.save.save.app_error", "translation": "Unable to save reaction" }, + { + "id": "store.sql_role.save.insert.app_error", + "translation": "Unable to save new role" + }, + { + "id": "store.sql_role.save.update.app_error", + "translation": "Unable to update role" + }, + { + "id": "store.sql_role.get.app_error", + "translation": "Unable to get role" + }, + { + "id": "store.sql_role.get_by_name.app_error", + "translation": "Unable to get role" + }, + { + "id": "store.sql_role.get_by_names.app_error", + "translation": "Unable to get roles" + }, { "id": "store.sql_session.analytics_session_count.app_error", "translation": "We couldn't count the sessions" diff --git a/model/authorization.go b/model/authorization.go deleted file mode 100644 index 9f4e36eab..000000000 --- a/model/authorization.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package model - -type Permission struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -type Role struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Permissions []string `json:"permissions"` -} - -var PERMISSION_INVITE_USER *Permission -var PERMISSION_ADD_USER_TO_TEAM *Permission -var PERMISSION_USE_SLASH_COMMANDS *Permission -var PERMISSION_MANAGE_SLASH_COMMANDS *Permission -var PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS *Permission -var PERMISSION_CREATE_PUBLIC_CHANNEL *Permission -var PERMISSION_CREATE_PRIVATE_CHANNEL *Permission -var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission -var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission -var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission -var PERMISSION_MANAGE_ROLES *Permission -var PERMISSION_MANAGE_TEAM_ROLES *Permission -var PERMISSION_MANAGE_CHANNEL_ROLES *Permission -var PERMISSION_CREATE_DIRECT_CHANNEL *Permission -var PERMISSION_CREATE_GROUP_CHANNEL *Permission -var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission -var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission -var PERMISSION_LIST_TEAM_CHANNELS *Permission -var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission -var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission -var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission -var PERMISSION_EDIT_OTHER_USERS *Permission -var PERMISSION_READ_CHANNEL *Permission -var PERMISSION_READ_PUBLIC_CHANNEL *Permission -var PERMISSION_PERMANENT_DELETE_USER *Permission -var PERMISSION_UPLOAD_FILE *Permission -var PERMISSION_GET_PUBLIC_LINK *Permission -var PERMISSION_MANAGE_WEBHOOKS *Permission -var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission -var PERMISSION_MANAGE_OAUTH *Permission -var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission -var PERMISSION_CREATE_POST *Permission -var PERMISSION_CREATE_POST_PUBLIC *Permission -var PERMISSION_EDIT_POST *Permission -var PERMISSION_EDIT_OTHERS_POSTS *Permission -var PERMISSION_DELETE_POST *Permission -var PERMISSION_DELETE_OTHERS_POSTS *Permission -var PERMISSION_REMOVE_USER_FROM_TEAM *Permission -var PERMISSION_CREATE_TEAM *Permission -var PERMISSION_MANAGE_TEAM *Permission -var PERMISSION_IMPORT_TEAM *Permission -var PERMISSION_VIEW_TEAM *Permission -var PERMISSION_LIST_USERS_WITHOUT_TEAM *Permission -var PERMISSION_MANAGE_JOBS *Permission -var PERMISSION_CREATE_USER_ACCESS_TOKEN *Permission -var PERMISSION_READ_USER_ACCESS_TOKEN *Permission -var PERMISSION_REVOKE_USER_ACCESS_TOKEN *Permission - -// General permission that encompases all system admin functions -// in the future this could be broken up to allow access to some -// admin functions but not others -var PERMISSION_MANAGE_SYSTEM *Permission - -const ( - SYSTEM_USER_ROLE_ID = "system_user" - SYSTEM_ADMIN_ROLE_ID = "system_admin" - SYSTEM_POST_ALL_ROLE_ID = "system_post_all" - SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public" - SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token" - - TEAM_USER_ROLE_ID = "team_user" - TEAM_ADMIN_ROLE_ID = "team_admin" - TEAM_POST_ALL_ROLE_ID = "team_post_all" - TEAM_POST_ALL_PUBLIC_ROLE_ID = "team_post_all_public" - - CHANNEL_USER_ROLE_ID = "channel_user" - CHANNEL_ADMIN_ROLE_ID = "channel_admin" - CHANNEL_GUEST_ROLE_ID = "guest" -) - -func initializePermissions() { - PERMISSION_INVITE_USER = &Permission{ - "invite_user", - "authentication.permissions.team_invite_user.name", - "authentication.permissions.team_invite_user.description", - } - PERMISSION_ADD_USER_TO_TEAM = &Permission{ - "add_user_to_team", - "authentication.permissions.add_user_to_team.name", - "authentication.permissions.add_user_to_team.description", - } - PERMISSION_USE_SLASH_COMMANDS = &Permission{ - "use_slash_commands", - "authentication.permissions.team_use_slash_commands.name", - "authentication.permissions.team_use_slash_commands.description", - } - PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{ - "manage_slash_commands", - "authentication.permissions.manage_slash_commands.name", - "authentication.permissions.manage_slash_commands.description", - } - PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{ - "manage_others_slash_commands", - "authentication.permissions.manage_others_slash_commands.name", - "authentication.permissions.manage_others_slash_commands.description", - } - PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{ - "create_public_channel", - "authentication.permissions.create_public_channel.name", - "authentication.permissions.create_public_channel.description", - } - PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{ - "create_private_channel", - "authentication.permissions.create_private_channel.name", - "authentication.permissions.create_private_channel.description", - } - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{ - "manage_public_channel_members", - "authentication.permissions.manage_public_channel_members.name", - "authentication.permissions.manage_public_channel_members.description", - } - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{ - "manage_private_channel_members", - "authentication.permissions.manage_private_channel_members.name", - "authentication.permissions.manage_private_channel_members.description", - } - PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{ - "assign_system_admin_role", - "authentication.permissions.assign_system_admin_role.name", - "authentication.permissions.assign_system_admin_role.description", - } - PERMISSION_MANAGE_ROLES = &Permission{ - "manage_roles", - "authentication.permissions.manage_roles.name", - "authentication.permissions.manage_roles.description", - } - PERMISSION_MANAGE_TEAM_ROLES = &Permission{ - "manage_team_roles", - "authentication.permissions.manage_team_roles.name", - "authentication.permissions.manage_team_roles.description", - } - PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ - "manage_channel_roles", - "authentication.permissions.manage_channel_roles.name", - "authentication.permissions.manage_channel_roles.description", - } - PERMISSION_MANAGE_SYSTEM = &Permission{ - "manage_system", - "authentication.permissions.manage_system.name", - "authentication.permissions.manage_system.description", - } - PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{ - "create_direct_channel", - "authentication.permissions.create_direct_channel.name", - "authentication.permissions.create_direct_channel.description", - } - PERMISSION_CREATE_GROUP_CHANNEL = &Permission{ - "create_group_channel", - "authentication.permissions.create_group_channel.name", - "authentication.permissions.create_group_channel.description", - } - PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ - "manage__publicchannel_properties", - "authentication.permissions.manage_public_channel_properties.name", - "authentication.permissions.manage_public_channel_properties.description", - } - PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{ - "manage_private_channel_properties", - "authentication.permissions.manage_private_channel_properties.name", - "authentication.permissions.manage_private_channel_properties.description", - } - PERMISSION_LIST_TEAM_CHANNELS = &Permission{ - "list_team_channels", - "authentication.permissions.list_team_channels.name", - "authentication.permissions.list_team_channels.description", - } - PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{ - "join_public_channels", - "authentication.permissions.join_public_channels.name", - "authentication.permissions.join_public_channels.description", - } - PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{ - "delete_public_channel", - "authentication.permissions.delete_public_channel.name", - "authentication.permissions.delete_public_channel.description", - } - PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{ - "delete_private_channel", - "authentication.permissions.delete_private_channel.name", - "authentication.permissions.delete_private_channel.description", - } - PERMISSION_EDIT_OTHER_USERS = &Permission{ - "edit_other_users", - "authentication.permissions.edit_other_users.name", - "authentication.permissions.edit_other_users.description", - } - PERMISSION_READ_CHANNEL = &Permission{ - "read_channel", - "authentication.permissions.read_channel.name", - "authentication.permissions.read_channel.description", - } - PERMISSION_READ_PUBLIC_CHANNEL = &Permission{ - "read_public_channel", - "authentication.permissions.read_public_channel.name", - "authentication.permissions.read_public_channel.description", - } - PERMISSION_PERMANENT_DELETE_USER = &Permission{ - "permanent_delete_user", - "authentication.permissions.permanent_delete_user.name", - "authentication.permissions.permanent_delete_user.description", - } - PERMISSION_UPLOAD_FILE = &Permission{ - "upload_file", - "authentication.permissions.upload_file.name", - "authentication.permissions.upload_file.description", - } - PERMISSION_GET_PUBLIC_LINK = &Permission{ - "get_public_link", - "authentication.permissions.get_public_link.name", - "authentication.permissions.get_public_link.description", - } - PERMISSION_MANAGE_WEBHOOKS = &Permission{ - "manage_webhooks", - "authentication.permissions.manage_webhooks.name", - "authentication.permissions.manage_webhooks.description", - } - PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{ - "manage_others_webhooks", - "authentication.permissions.manage_others_webhooks.name", - "authentication.permissions.manage_others_webhooks.description", - } - PERMISSION_MANAGE_OAUTH = &Permission{ - "manage_oauth", - "authentication.permissions.manage_oauth.name", - "authentication.permissions.manage_oauth.description", - } - PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{ - "manage_sytem_wide_oauth", - "authentication.permissions.manage_sytem_wide_oauth.name", - "authentication.permissions.manage_sytem_wide_oauth.description", - } - PERMISSION_CREATE_POST = &Permission{ - "create_post", - "authentication.permissions.create_post.name", - "authentication.permissions.create_post.description", - } - PERMISSION_CREATE_POST_PUBLIC = &Permission{ - "create_post_public", - "authentication.permissions.create_post_public.name", - "authentication.permissions.create_post_public.description", - } - PERMISSION_EDIT_POST = &Permission{ - "edit_post", - "authentication.permissions.edit_post.name", - "authentication.permissions.edit_post.description", - } - PERMISSION_EDIT_OTHERS_POSTS = &Permission{ - "edit_others_posts", - "authentication.permissions.edit_others_posts.name", - "authentication.permissions.edit_others_posts.description", - } - PERMISSION_DELETE_POST = &Permission{ - "delete_post", - "authentication.permissions.delete_post.name", - "authentication.permissions.delete_post.description", - } - PERMISSION_DELETE_OTHERS_POSTS = &Permission{ - "delete_others_posts", - "authentication.permissions.delete_others_posts.name", - "authentication.permissions.delete_others_posts.description", - } - PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ - "remove_user_from_team", - "authentication.permissions.remove_user_from_team.name", - "authentication.permissions.remove_user_from_team.description", - } - PERMISSION_CREATE_TEAM = &Permission{ - "create_team", - "authentication.permissions.create_team.name", - "authentication.permissions.create_team.description", - } - PERMISSION_MANAGE_TEAM = &Permission{ - "manage_team", - "authentication.permissions.manage_team.name", - "authentication.permissions.manage_team.description", - } - PERMISSION_IMPORT_TEAM = &Permission{ - "import_team", - "authentication.permissions.import_team.name", - "authentication.permissions.import_team.description", - } - PERMISSION_VIEW_TEAM = &Permission{ - "view_team", - "authentication.permissions.view_team.name", - "authentication.permissions.view_team.description", - } - PERMISSION_LIST_USERS_WITHOUT_TEAM = &Permission{ - "list_users_without_team", - "authentication.permissions.list_users_without_team.name", - "authentication.permissions.list_users_without_team.description", - } - PERMISSION_CREATE_USER_ACCESS_TOKEN = &Permission{ - "create_user_access_token", - "authentication.permissions.create_user_access_token.name", - "authentication.permissions.create_user_access_token.description", - } - PERMISSION_READ_USER_ACCESS_TOKEN = &Permission{ - "read_user_access_token", - "authentication.permissions.read_user_access_token.name", - "authentication.permissions.read_user_access_token.description", - } - PERMISSION_REVOKE_USER_ACCESS_TOKEN = &Permission{ - "revoke_user_access_token", - "authentication.permissions.revoke_user_access_token.name", - "authentication.permissions.revoke_user_access_token.description", - } - PERMISSION_MANAGE_JOBS = &Permission{ - "manage_jobs", - "authentication.permisssions.manage_jobs.name", - "authentication.permisssions.manage_jobs.description", - } -} - -var DefaultRoles map[string]*Role - -func initializeDefaultRoles() { - DefaultRoles = make(map[string]*Role) - - DefaultRoles[CHANNEL_USER_ROLE_ID] = &Role{ - "channel_user", - "authentication.roles.channel_user.name", - "authentication.roles.channel_user.description", - []string{ - PERMISSION_READ_CHANNEL.Id, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, - PERMISSION_UPLOAD_FILE.Id, - PERMISSION_GET_PUBLIC_LINK.Id, - PERMISSION_CREATE_POST.Id, - PERMISSION_EDIT_POST.Id, - PERMISSION_USE_SLASH_COMMANDS.Id, - }, - } - - DefaultRoles[CHANNEL_ADMIN_ROLE_ID] = &Role{ - "channel_admin", - "authentication.roles.channel_admin.name", - "authentication.roles.channel_admin.description", - []string{ - PERMISSION_MANAGE_CHANNEL_ROLES.Id, - }, - } - - DefaultRoles[CHANNEL_GUEST_ROLE_ID] = &Role{ - "guest", - "authentication.roles.global_guest.name", - "authentication.roles.global_guest.description", - []string{}, - } - - DefaultRoles[TEAM_USER_ROLE_ID] = &Role{ - "team_user", - "authentication.roles.team_user.name", - "authentication.roles.team_user.description", - []string{ - PERMISSION_LIST_TEAM_CHANNELS.Id, - PERMISSION_JOIN_PUBLIC_CHANNELS.Id, - PERMISSION_READ_PUBLIC_CHANNEL.Id, - PERMISSION_VIEW_TEAM.Id, - }, - } - - DefaultRoles[TEAM_POST_ALL_ROLE_ID] = &Role{ - "team_post_all", - "authentication.roles.team_post_all.name", - "authentication.roles.team_post_all.description", - []string{ - PERMISSION_CREATE_POST.Id, - }, - } - - DefaultRoles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ - "team_post_all_public", - "authentication.roles.team_post_all_public.name", - "authentication.roles.team_post_all_public.description", - []string{ - PERMISSION_CREATE_POST_PUBLIC.Id, - }, - } - - DefaultRoles[TEAM_ADMIN_ROLE_ID] = &Role{ - "team_admin", - "authentication.roles.team_admin.name", - "authentication.roles.team_admin.description", - []string{ - PERMISSION_EDIT_OTHERS_POSTS.Id, - PERMISSION_REMOVE_USER_FROM_TEAM.Id, - PERMISSION_MANAGE_TEAM.Id, - PERMISSION_IMPORT_TEAM.Id, - PERMISSION_MANAGE_TEAM_ROLES.Id, - PERMISSION_MANAGE_CHANNEL_ROLES.Id, - PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, - PERMISSION_MANAGE_SLASH_COMMANDS.Id, - PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, - PERMISSION_MANAGE_WEBHOOKS.Id, - }, - } - - DefaultRoles[SYSTEM_USER_ROLE_ID] = &Role{ - "system_user", - "authentication.roles.global_user.name", - "authentication.roles.global_user.description", - []string{ - PERMISSION_CREATE_DIRECT_CHANNEL.Id, - PERMISSION_CREATE_GROUP_CHANNEL.Id, - PERMISSION_PERMANENT_DELETE_USER.Id, - }, - } - - DefaultRoles[SYSTEM_POST_ALL_ROLE_ID] = &Role{ - "system_post_all", - "authentication.roles.system_post_all.name", - "authentication.roles.system_post_all.description", - []string{ - PERMISSION_CREATE_POST.Id, - }, - } - - DefaultRoles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ - "system_post_all_public", - "authentication.roles.system_post_all_public.name", - "authentication.roles.system_post_all_public.description", - []string{ - PERMISSION_CREATE_POST_PUBLIC.Id, - }, - } - - DefaultRoles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{ - "system_user_access_token", - "authentication.roles.system_user_access_token.name", - "authentication.roles.system_user_access_token.description", - []string{ - PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, - PERMISSION_READ_USER_ACCESS_TOKEN.Id, - PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, - }, - } - - DefaultRoles[SYSTEM_ADMIN_ROLE_ID] = &Role{ - "system_admin", - "authentication.roles.global_admin.name", - "authentication.roles.global_admin.description", - // System admins can do anything channel and team admins can do - // plus everything members of teams and channels can do to all teams - // and channels on the system - append( - append( - append( - append( - []string{ - PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, - PERMISSION_MANAGE_SYSTEM.Id, - PERMISSION_MANAGE_ROLES.Id, - PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, - PERMISSION_DELETE_PUBLIC_CHANNEL.Id, - PERMISSION_CREATE_PUBLIC_CHANNEL.Id, - PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, - PERMISSION_DELETE_PRIVATE_CHANNEL.Id, - PERMISSION_CREATE_PRIVATE_CHANNEL.Id, - PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id, - PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, - PERMISSION_EDIT_OTHER_USERS.Id, - PERMISSION_MANAGE_OAUTH.Id, - PERMISSION_INVITE_USER.Id, - PERMISSION_DELETE_POST.Id, - PERMISSION_DELETE_OTHERS_POSTS.Id, - PERMISSION_CREATE_TEAM.Id, - PERMISSION_ADD_USER_TO_TEAM.Id, - PERMISSION_LIST_USERS_WITHOUT_TEAM.Id, - PERMISSION_MANAGE_JOBS.Id, - PERMISSION_CREATE_POST_PUBLIC.Id, - PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, - PERMISSION_READ_USER_ACCESS_TOKEN.Id, - PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, - }, - DefaultRoles[TEAM_USER_ROLE_ID].Permissions..., - ), - DefaultRoles[CHANNEL_USER_ROLE_ID].Permissions..., - ), - DefaultRoles[TEAM_ADMIN_ROLE_ID].Permissions..., - ), - DefaultRoles[CHANNEL_ADMIN_ROLE_ID].Permissions..., - ), - } -} - -func RoleIdsToString(roles []string) string { - output := "" - for _, role := range roles { - output += role + ", " - } - - if output == "" { - return "[]" - } - - return output[:len(output)-1] -} - -func init() { - initializePermissions() - initializeDefaultRoles() -} diff --git a/model/client4.go b/model/client4.go index 0694ecbdf..55179bdf9 100644 --- a/model/client4.go +++ b/model/client4.go @@ -306,6 +306,10 @@ func (c *Client4) GetJobsRoute() string { return fmt.Sprintf("/jobs") } +func (c *Client4) GetRolesRoute() string { + return fmt.Sprintf("/roles") +} + func (c *Client4) GetAnalyticsRoute() string { return fmt.Sprintf("/analytics") } @@ -3204,6 +3208,48 @@ func (c *Client4) CancelJob(jobId string) (bool, *Response) { } } +// Roles Section + +// GetRole gets a single role by ID. +func (c *Client4) GetRole(id string) (*Role, *Response) { + if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/%v", id), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return RoleFromJson(r.Body), BuildResponse(r) + } +} + +// GetRoleByName gets a single role by Name. +func (c *Client4) GetRoleByName(name string) (*Role, *Response) { + if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/name/%v", name), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return RoleFromJson(r.Body), BuildResponse(r) + } +} + +// GetRolesByNames returns a list of roles based on the provided role names. +func (c *Client4) GetRolesByNames(roleNames []string) ([]*Role, *Response) { + if r, err := c.DoApiPost(c.GetRolesRoute()+"/names", ArrayToJson(roleNames)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return RoleListFromJson(r.Body), BuildResponse(r) + } +} + +// PatchRole partially updates a role in the system. Any missing fields are not updated. +func (c *Client4) PatchRole(roleId string, patch *RolePatch) (*Role, *Response) { + if r, err := c.DoApiPut(c.GetRolesRoute()+fmt.Sprintf("/%v/patch", roleId), patch.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return RoleFromJson(r.Body), BuildResponse(r) + } +} + // Plugin Section // UploadPlugin takes an io.Reader stream pointing to the contents of a .tar.gz plugin. diff --git a/model/cluster_message.go b/model/cluster_message.go index a6dec2e7f..9cf0e4d55 100644 --- a/model/cluster_message.go +++ b/model/cluster_message.go @@ -21,6 +21,7 @@ const ( CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL = "inv_channel" CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user" CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles" CLUSTER_SEND_BEST_EFFORT = "best_effort" CLUSTER_SEND_RELIABLE = "reliable" diff --git a/model/permission.go b/model/permission.go new file mode 100644 index 000000000..0dc09f49a --- /dev/null +++ b/model/permission.go @@ -0,0 +1,418 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +const ( + PERMISSION_SCOPE_SYSTEM = "system_scope" + PERMISSION_SCOPE_TEAM = "team_scope" + PERMISSION_SCOPE_CHANNEL = "channel_scope" +) + +type Permission struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Scope string `json:"scope"` +} + +var PERMISSION_INVITE_USER *Permission +var PERMISSION_ADD_USER_TO_TEAM *Permission +var PERMISSION_USE_SLASH_COMMANDS *Permission +var PERMISSION_MANAGE_SLASH_COMMANDS *Permission +var PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS *Permission +var PERMISSION_CREATE_PUBLIC_CHANNEL *Permission +var PERMISSION_CREATE_PRIVATE_CHANNEL *Permission +var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission +var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission +var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission +var PERMISSION_MANAGE_ROLES *Permission +var PERMISSION_MANAGE_TEAM_ROLES *Permission +var PERMISSION_MANAGE_CHANNEL_ROLES *Permission +var PERMISSION_CREATE_DIRECT_CHANNEL *Permission +var PERMISSION_CREATE_GROUP_CHANNEL *Permission +var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission +var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission +var PERMISSION_LIST_TEAM_CHANNELS *Permission +var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission +var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission +var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission +var PERMISSION_EDIT_OTHER_USERS *Permission +var PERMISSION_READ_CHANNEL *Permission +var PERMISSION_READ_PUBLIC_CHANNEL *Permission +var PERMISSION_PERMANENT_DELETE_USER *Permission +var PERMISSION_UPLOAD_FILE *Permission +var PERMISSION_GET_PUBLIC_LINK *Permission +var PERMISSION_MANAGE_WEBHOOKS *Permission +var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission +var PERMISSION_MANAGE_OAUTH *Permission +var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission +var PERMISSION_CREATE_POST *Permission +var PERMISSION_CREATE_POST_PUBLIC *Permission +var PERMISSION_EDIT_POST *Permission +var PERMISSION_EDIT_OTHERS_POSTS *Permission +var PERMISSION_DELETE_POST *Permission +var PERMISSION_DELETE_OTHERS_POSTS *Permission +var PERMISSION_REMOVE_USER_FROM_TEAM *Permission +var PERMISSION_CREATE_TEAM *Permission +var PERMISSION_MANAGE_TEAM *Permission +var PERMISSION_IMPORT_TEAM *Permission +var PERMISSION_VIEW_TEAM *Permission +var PERMISSION_LIST_USERS_WITHOUT_TEAM *Permission +var PERMISSION_MANAGE_JOBS *Permission +var PERMISSION_CREATE_USER_ACCESS_TOKEN *Permission +var PERMISSION_READ_USER_ACCESS_TOKEN *Permission +var PERMISSION_REVOKE_USER_ACCESS_TOKEN *Permission + +// General permission that encompasses all system admin functions +// in the future this could be broken up to allow access to some +// admin functions but not others +var PERMISSION_MANAGE_SYSTEM *Permission + +var ALL_PERMISSIONS []*Permission + +func initializePermissions() { + PERMISSION_INVITE_USER = &Permission{ + "invite_user", + "authentication.permissions.team_invite_user.name", + "authentication.permissions.team_invite_user.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_ADD_USER_TO_TEAM = &Permission{ + "add_user_to_team", + "authentication.permissions.add_user_to_team.name", + "authentication.permissions.add_user_to_team.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_USE_SLASH_COMMANDS = &Permission{ + "use_slash_commands", + "authentication.permissions.team_use_slash_commands.name", + "authentication.permissions.team_use_slash_commands.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{ + "manage_slash_commands", + "authentication.permissions.manage_slash_commands.name", + "authentication.permissions.manage_slash_commands.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{ + "manage_others_slash_commands", + "authentication.permissions.manage_others_slash_commands.name", + "authentication.permissions.manage_others_slash_commands.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{ + "create_public_channel", + "authentication.permissions.create_public_channel.name", + "authentication.permissions.create_public_channel.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{ + "create_private_channel", + "authentication.permissions.create_private_channel.name", + "authentication.permissions.create_private_channel.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{ + "manage_public_channel_members", + "authentication.permissions.manage_public_channel_members.name", + "authentication.permissions.manage_public_channel_members.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{ + "manage_private_channel_members", + "authentication.permissions.manage_private_channel_members.name", + "authentication.permissions.manage_private_channel_members.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{ + "assign_system_admin_role", + "authentication.permissions.assign_system_admin_role.name", + "authentication.permissions.assign_system_admin_role.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_ROLES = &Permission{ + "manage_roles", + "authentication.permissions.manage_roles.name", + "authentication.permissions.manage_roles.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_TEAM_ROLES = &Permission{ + "manage_team_roles", + "authentication.permissions.manage_team_roles.name", + "authentication.permissions.manage_team_roles.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ + "manage_channel_roles", + "authentication.permissions.manage_channel_roles.name", + "authentication.permissions.manage_channel_roles.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_MANAGE_SYSTEM = &Permission{ + "manage_system", + "authentication.permissions.manage_system.name", + "authentication.permissions.manage_system.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{ + "create_direct_channel", + "authentication.permissions.create_direct_channel.name", + "authentication.permissions.create_direct_channel.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_CREATE_GROUP_CHANNEL = &Permission{ + "create_group_channel", + "authentication.permissions.create_group_channel.name", + "authentication.permissions.create_group_channel.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ + "manage_public_channel_properties", + "authentication.permissions.manage_public_channel_properties.name", + "authentication.permissions.manage_public_channel_properties.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{ + "manage_private_channel_properties", + "authentication.permissions.manage_private_channel_properties.name", + "authentication.permissions.manage_private_channel_properties.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_LIST_TEAM_CHANNELS = &Permission{ + "list_team_channels", + "authentication.permissions.list_team_channels.name", + "authentication.permissions.list_team_channels.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{ + "join_public_channels", + "authentication.permissions.join_public_channels.name", + "authentication.permissions.join_public_channels.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{ + "delete_public_channel", + "authentication.permissions.delete_public_channel.name", + "authentication.permissions.delete_public_channel.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{ + "delete_private_channel", + "authentication.permissions.delete_private_channel.name", + "authentication.permissions.delete_private_channel.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_EDIT_OTHER_USERS = &Permission{ + "edit_other_users", + "authentication.permissions.edit_other_users.name", + "authentication.permissions.edit_other_users.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_READ_CHANNEL = &Permission{ + "read_channel", + "authentication.permissions.read_channel.name", + "authentication.permissions.read_channel.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_READ_PUBLIC_CHANNEL = &Permission{ + "read_public_channel", + "authentication.permissions.read_public_channel.name", + "authentication.permissions.read_public_channel.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_PERMANENT_DELETE_USER = &Permission{ + "permanent_delete_user", + "authentication.permissions.permanent_delete_user.name", + "authentication.permissions.permanent_delete_user.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_UPLOAD_FILE = &Permission{ + "upload_file", + "authentication.permissions.upload_file.name", + "authentication.permissions.upload_file.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_GET_PUBLIC_LINK = &Permission{ + "get_public_link", + "authentication.permissions.get_public_link.name", + "authentication.permissions.get_public_link.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_WEBHOOKS = &Permission{ + "manage_webhooks", + "authentication.permissions.manage_webhooks.name", + "authentication.permissions.manage_webhooks.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{ + "manage_others_webhooks", + "authentication.permissions.manage_others_webhooks.name", + "authentication.permissions.manage_others_webhooks.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_MANAGE_OAUTH = &Permission{ + "manage_oauth", + "authentication.permissions.manage_oauth.name", + "authentication.permissions.manage_oauth.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{ + "manage_sytem_wide_oauth", + "authentication.permissions.manage_sytem_wide_oauth.name", + "authentication.permissions.manage_sytem_wide_oauth.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_CREATE_POST = &Permission{ + "create_post", + "authentication.permissions.create_post.name", + "authentication.permissions.create_post.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_CREATE_POST_PUBLIC = &Permission{ + "create_post_public", + "authentication.permissions.create_post_public.name", + "authentication.permissions.create_post_public.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_EDIT_POST = &Permission{ + "edit_post", + "authentication.permissions.edit_post.name", + "authentication.permissions.edit_post.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_EDIT_OTHERS_POSTS = &Permission{ + "edit_others_posts", + "authentication.permissions.edit_others_posts.name", + "authentication.permissions.edit_others_posts.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_DELETE_POST = &Permission{ + "delete_post", + "authentication.permissions.delete_post.name", + "authentication.permissions.delete_post.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_DELETE_OTHERS_POSTS = &Permission{ + "delete_others_posts", + "authentication.permissions.delete_others_posts.name", + "authentication.permissions.delete_others_posts.description", + PERMISSION_SCOPE_CHANNEL, + } + PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ + "remove_user_from_team", + "authentication.permissions.remove_user_from_team.name", + "authentication.permissions.remove_user_from_team.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_CREATE_TEAM = &Permission{ + "create_team", + "authentication.permissions.create_team.name", + "authentication.permissions.create_team.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_TEAM = &Permission{ + "manage_team", + "authentication.permissions.manage_team.name", + "authentication.permissions.manage_team.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_IMPORT_TEAM = &Permission{ + "import_team", + "authentication.permissions.import_team.name", + "authentication.permissions.import_team.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_VIEW_TEAM = &Permission{ + "view_team", + "authentication.permissions.view_team.name", + "authentication.permissions.view_team.description", + PERMISSION_SCOPE_TEAM, + } + PERMISSION_LIST_USERS_WITHOUT_TEAM = &Permission{ + "list_users_without_team", + "authentication.permissions.list_users_without_team.name", + "authentication.permissions.list_users_without_team.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_CREATE_USER_ACCESS_TOKEN = &Permission{ + "create_user_access_token", + "authentication.permissions.create_user_access_token.name", + "authentication.permissions.create_user_access_token.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_READ_USER_ACCESS_TOKEN = &Permission{ + "read_user_access_token", + "authentication.permissions.read_user_access_token.name", + "authentication.permissions.read_user_access_token.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_REVOKE_USER_ACCESS_TOKEN = &Permission{ + "revoke_user_access_token", + "authentication.permissions.revoke_user_access_token.name", + "authentication.permissions.revoke_user_access_token.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_MANAGE_JOBS = &Permission{ + "manage_jobs", + "authentication.permisssions.manage_jobs.name", + "authentication.permisssions.manage_jobs.description", + PERMISSION_SCOPE_SYSTEM, + } + + ALL_PERMISSIONS = []*Permission{ + PERMISSION_INVITE_USER, + PERMISSION_ADD_USER_TO_TEAM, + PERMISSION_USE_SLASH_COMMANDS, + PERMISSION_MANAGE_SLASH_COMMANDS, + PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS, + PERMISSION_CREATE_PUBLIC_CHANNEL, + PERMISSION_CREATE_PRIVATE_CHANNEL, + PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS, + PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS, + PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE, + PERMISSION_MANAGE_ROLES, + PERMISSION_MANAGE_TEAM_ROLES, + PERMISSION_MANAGE_CHANNEL_ROLES, + PERMISSION_CREATE_DIRECT_CHANNEL, + PERMISSION_CREATE_GROUP_CHANNEL, + PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES, + PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES, + PERMISSION_LIST_TEAM_CHANNELS, + PERMISSION_JOIN_PUBLIC_CHANNELS, + PERMISSION_DELETE_PUBLIC_CHANNEL, + PERMISSION_DELETE_PRIVATE_CHANNEL, + PERMISSION_EDIT_OTHER_USERS, + PERMISSION_READ_CHANNEL, + PERMISSION_READ_PUBLIC_CHANNEL, + PERMISSION_PERMANENT_DELETE_USER, + PERMISSION_UPLOAD_FILE, + PERMISSION_GET_PUBLIC_LINK, + PERMISSION_MANAGE_WEBHOOKS, + PERMISSION_MANAGE_OTHERS_WEBHOOKS, + PERMISSION_MANAGE_OAUTH, + PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH, + PERMISSION_CREATE_POST, + PERMISSION_CREATE_POST_PUBLIC, + PERMISSION_EDIT_POST, + PERMISSION_EDIT_OTHERS_POSTS, + PERMISSION_DELETE_POST, + PERMISSION_DELETE_OTHERS_POSTS, + PERMISSION_REMOVE_USER_FROM_TEAM, + PERMISSION_CREATE_TEAM, + PERMISSION_MANAGE_TEAM, + PERMISSION_IMPORT_TEAM, + PERMISSION_VIEW_TEAM, + PERMISSION_LIST_USERS_WITHOUT_TEAM, + PERMISSION_MANAGE_JOBS, + PERMISSION_CREATE_USER_ACCESS_TOKEN, + PERMISSION_READ_USER_ACCESS_TOKEN, + PERMISSION_REVOKE_USER_ACCESS_TOKEN, + PERMISSION_MANAGE_SYSTEM, + } +} + +func init() { + initializePermissions() +} diff --git a/model/permission_test.go b/model/permission_test.go new file mode 100644 index 000000000..cef72af90 --- /dev/null +++ b/model/permission_test.go @@ -0,0 +1,18 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// This is a test to ensure that we don't accidentally add more permissions than can fit +// in the database column for role permissions. +func TestPermissionsLength(t *testing.T) { + permissionsString := "" + for _, permission := range ALL_PERMISSIONS { + permissionsString += " " + permission.Id + } + + assert.True(t, len(permissionsString) < 4096) +} diff --git a/model/role.go b/model/role.go new file mode 100644 index 000000000..254513ae0 --- /dev/null +++ b/model/role.go @@ -0,0 +1,310 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "strings" +) + +const ( + SYSTEM_USER_ROLE_ID = "system_user" + SYSTEM_ADMIN_ROLE_ID = "system_admin" + SYSTEM_POST_ALL_ROLE_ID = "system_post_all" + SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public" + SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token" + + TEAM_USER_ROLE_ID = "team_user" + TEAM_ADMIN_ROLE_ID = "team_admin" + TEAM_POST_ALL_ROLE_ID = "team_post_all" + TEAM_POST_ALL_PUBLIC_ROLE_ID = "team_post_all_public" + + CHANNEL_USER_ROLE_ID = "channel_user" + CHANNEL_ADMIN_ROLE_ID = "channel_admin" + + ROLE_NAME_MAX_LENGTH = 64 + ROLE_DISPLAY_NAME_MAX_LENGTH = 128 + ROLE_DESCRIPTION_MAX_LENGTH = 1024 +) + +type Role struct { + Id string `json:"id"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + Permissions []string `json:"permissions"` + SchemeManaged bool `json:"scheme_managed"` +} + +type RolePatch struct { + Permissions *[]string `json:"permissions"` +} + +func (role *Role) ToJson() string { + b, _ := json.Marshal(role) + return string(b) +} + +func RoleFromJson(data io.Reader) *Role { + var role *Role + json.NewDecoder(data).Decode(&role) + return role +} + +func RoleListToJson(r []*Role) string { + b, _ := json.Marshal(r) + return string(b) +} + +func RoleListFromJson(data io.Reader) []*Role { + var roles []*Role + json.NewDecoder(data).Decode(&roles) + return roles +} + +func (r *RolePatch) ToJson() string { + b, _ := json.Marshal(r) + return string(b) +} + +func RolePatchFromJson(data io.Reader) *RolePatch { + var rolePatch *RolePatch + json.NewDecoder(data).Decode(&rolePatch) + return rolePatch +} + +func (o *Role) Patch(patch *RolePatch) { + if patch.Permissions != nil { + o.Permissions = *patch.Permissions + } +} + +func (role *Role) IsValid() bool { + if len(role.Id) != 26 { + return false + } + + return role.IsValidWithoutId() +} + +func (role *Role) IsValidWithoutId() bool { + if !IsValidRoleName(role.Name) { + return false + } + + if len(role.DisplayName) == 0 || len(role.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH { + return false + } + + if len(role.Description) > ROLE_DESCRIPTION_MAX_LENGTH { + return false + } + + for _, permission := range role.Permissions { + permissionValidated := false + for _, p := range ALL_PERMISSIONS { + if permission == p.Id { + permissionValidated = true + break + } + } + + if !permissionValidated { + return false + } + } + + return true +} + +func IsValidRoleName(roleName string) bool { + if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH { + return false + } + + if strings.TrimLeft(roleName, "abcdefghijklmnopqrstuvwxyz0123456789_") != "" { + return false + } + + return true +} + +func MakeDefaultRoles() map[string]*Role { + roles := make(map[string]*Role) + + roles[CHANNEL_USER_ROLE_ID] = &Role{ + Name: "channel_user", + DisplayName: "authentication.roles.channel_user.name", + Description: "authentication.roles.channel_user.description", + Permissions: []string{ + PERMISSION_READ_CHANNEL.Id, + PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + PERMISSION_UPLOAD_FILE.Id, + PERMISSION_GET_PUBLIC_LINK.Id, + PERMISSION_CREATE_POST.Id, + PERMISSION_EDIT_POST.Id, + PERMISSION_USE_SLASH_COMMANDS.Id, + }, + SchemeManaged: true, + } + + roles[CHANNEL_ADMIN_ROLE_ID] = &Role{ + Name: "channel_admin", + DisplayName: "authentication.roles.channel_admin.name", + Description: "authentication.roles.channel_admin.description", + Permissions: []string{ + PERMISSION_MANAGE_CHANNEL_ROLES.Id, + }, + SchemeManaged: true, + } + + roles[TEAM_USER_ROLE_ID] = &Role{ + Name: "team_user", + DisplayName: "authentication.roles.team_user.name", + Description: "authentication.roles.team_user.description", + Permissions: []string{ + PERMISSION_LIST_TEAM_CHANNELS.Id, + PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + PERMISSION_READ_PUBLIC_CHANNEL.Id, + PERMISSION_VIEW_TEAM.Id, + }, + SchemeManaged: true, + } + + roles[TEAM_POST_ALL_ROLE_ID] = &Role{ + Name: "team_post_all", + DisplayName: "authentication.roles.team_post_all.name", + Description: "authentication.roles.team_post_all.description", + Permissions: []string{ + PERMISSION_CREATE_POST.Id, + }, + SchemeManaged: true, + } + + roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ + Name: "team_post_all_public", + DisplayName: "authentication.roles.team_post_all_public.name", + Description: "authentication.roles.team_post_all_public.description", + Permissions: []string{ + PERMISSION_CREATE_POST_PUBLIC.Id, + }, + SchemeManaged: true, + } + + roles[TEAM_ADMIN_ROLE_ID] = &Role{ + Name: "team_admin", + DisplayName: "authentication.roles.team_admin.name", + Description: "authentication.roles.team_admin.description", + Permissions: []string{ + PERMISSION_EDIT_OTHERS_POSTS.Id, + PERMISSION_REMOVE_USER_FROM_TEAM.Id, + PERMISSION_MANAGE_TEAM.Id, + PERMISSION_IMPORT_TEAM.Id, + PERMISSION_MANAGE_TEAM_ROLES.Id, + PERMISSION_MANAGE_CHANNEL_ROLES.Id, + PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + PERMISSION_MANAGE_SLASH_COMMANDS.Id, + PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, + PERMISSION_MANAGE_WEBHOOKS.Id, + }, + SchemeManaged: true, + } + + roles[SYSTEM_USER_ROLE_ID] = &Role{ + Name: "system_user", + DisplayName: "authentication.roles.global_user.name", + Description: "authentication.roles.global_user.description", + Permissions: []string{ + PERMISSION_CREATE_DIRECT_CHANNEL.Id, + PERMISSION_CREATE_GROUP_CHANNEL.Id, + PERMISSION_PERMANENT_DELETE_USER.Id, + }, + SchemeManaged: true, + } + + roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{ + Name: "system_post_all", + DisplayName: "authentication.roles.system_post_all.name", + Description: "authentication.roles.system_post_all.description", + Permissions: []string{ + PERMISSION_CREATE_POST.Id, + }, + SchemeManaged: true, + } + + roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ + Name: "system_post_all_public", + DisplayName: "authentication.roles.system_post_all_public.name", + Description: "authentication.roles.system_post_all_public.description", + Permissions: []string{ + PERMISSION_CREATE_POST_PUBLIC.Id, + }, + SchemeManaged: true, + } + + roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{ + Name: "system_user_access_token", + DisplayName: "authentication.roles.system_user_access_token.name", + Description: "authentication.roles.system_user_access_token.description", + Permissions: []string{ + PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + PERMISSION_READ_USER_ACCESS_TOKEN.Id, + PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + }, + SchemeManaged: true, + } + + roles[SYSTEM_ADMIN_ROLE_ID] = &Role{ + Name: "system_admin", + DisplayName: "authentication.roles.global_admin.name", + Description: "authentication.roles.global_admin.description", + // System admins can do anything channel and team admins can do + // plus everything members of teams and channels can do to all teams + // and channels on the system + Permissions: append( + append( + append( + append( + []string{ + PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, + PERMISSION_MANAGE_SYSTEM.Id, + PERMISSION_MANAGE_ROLES.Id, + PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, + PERMISSION_DELETE_PUBLIC_CHANNEL.Id, + PERMISSION_CREATE_PUBLIC_CHANNEL.Id, + PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, + PERMISSION_DELETE_PRIVATE_CHANNEL.Id, + PERMISSION_CREATE_PRIVATE_CHANNEL.Id, + PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id, + PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, + PERMISSION_EDIT_OTHER_USERS.Id, + PERMISSION_MANAGE_OAUTH.Id, + PERMISSION_INVITE_USER.Id, + PERMISSION_DELETE_POST.Id, + PERMISSION_DELETE_OTHERS_POSTS.Id, + PERMISSION_CREATE_TEAM.Id, + PERMISSION_ADD_USER_TO_TEAM.Id, + PERMISSION_LIST_USERS_WITHOUT_TEAM.Id, + PERMISSION_MANAGE_JOBS.Id, + PERMISSION_CREATE_POST_PUBLIC.Id, + PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + PERMISSION_READ_USER_ACCESS_TOKEN.Id, + PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + }, + roles[TEAM_USER_ROLE_ID].Permissions..., + ), + roles[CHANNEL_USER_ROLE_ID].Permissions..., + ), + roles[TEAM_ADMIN_ROLE_ID].Permissions..., + ), + roles[CHANNEL_ADMIN_ROLE_ID].Permissions..., + ), + SchemeManaged: true, + } + + return roles +} diff --git a/model/user.go b/model/user.go index 4c08eac24..556ebb1f7 100644 --- a/model/user.go +++ b/model/user.go @@ -432,7 +432,7 @@ func IsValidUserRoles(userRoles string) bool { roles := strings.Fields(userRoles) for _, r := range roles { - if !isValidRole(r) { + if !IsValidRoleName(r) { return false } } @@ -445,11 +445,6 @@ func IsValidUserRoles(userRoles string) bool { return true } -func isValidRole(roleId string) bool { - _, ok := DefaultRoles[roleId] - return ok -} - // Make sure you acually want to use this function. In context.go there are functions to check permissions // This function should not be used to check permissions. func (u *User) IsInRole(inRole string) bool { diff --git a/model/user_test.go b/model/user_test.go index c91051532..5f97b3ff2 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -263,11 +263,11 @@ func TestCleanUsername(t *testing.T) { func TestRoles(t *testing.T) { - if IsValidUserRoles("admin") { + if !IsValidUserRoles("team_user") { t.Fatal() } - if IsValidUserRoles("junk") { + if IsValidUserRoles("system_admin") { t.Fatal() } diff --git a/model/websocket_message.go b/model/websocket_message.go index bf2535dc3..d0e8cb039 100644 --- a/model/websocket_message.go +++ b/model/websocket_message.go @@ -42,6 +42,7 @@ const ( WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed" WEBSOCKET_EVENT_PLUGIN_ACTIVATED = "plugin_activated" // EXPERIMENTAL - SUBJECT TO CHANGE WEBSOCKET_EVENT_PLUGIN_DEACTIVATED = "plugin_deactivated" // EXPERIMENTAL - SUBJECT TO CHANGE + WEBSOCKET_EVENT_ROLE_UPDATED = "role_updated" ) type WebSocketMessage interface { diff --git a/store/layered_store.go b/store/layered_store.go index 65b4670c0..cac0f61d3 100644 --- a/store/layered_store.go +++ b/store/layered_store.go @@ -23,6 +23,7 @@ type LayeredStoreDatabaseLayer interface { type LayeredStore struct { TmpContext context.Context ReactionStore ReactionStore + RoleStore RoleStore DatabaseLayer LayeredStoreDatabaseLayer LocalCacheLayer *LocalCacheSupplier RedisLayer *RedisSupplier @@ -37,6 +38,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn } store.ReactionStore = &LayeredReactionStore{store} + store.RoleStore = &LayeredRoleStore{store} // Setup the chain if ENABLE_EXPERIMENTAL_REDIS { @@ -161,6 +163,10 @@ func (s *LayeredStore) Plugin() PluginStore { return s.DatabaseLayer.Plugin() } +func (s *LayeredStore) Role() RoleStore { + return s.RoleStore +} + func (s *LayeredStore) MarkSystemRanUnitTests() { s.DatabaseLayer.MarkSystemRanUnitTests() } @@ -218,3 +224,31 @@ func (s *LayeredReactionStore) PermanentDeleteBatch(endTime int64, limit int64) return supplier.ReactionPermanentDeleteBatch(s.TmpContext, endTime, limit) }) } + +type LayeredRoleStore struct { + *LayeredStore +} + +func (s *LayeredRoleStore) Save(role *model.Role) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleSave(s.TmpContext, role) + }) +} + +func (s *LayeredRoleStore) Get(roleId string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGet(s.TmpContext, roleId) + }) +} + +func (s *LayeredRoleStore) GetByName(name string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGetByName(s.TmpContext, name) + }) +} + +func (s *LayeredRoleStore) GetByNames(names []string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGetByNames(s.TmpContext, names) + }) +} diff --git a/store/layered_store_supplier.go b/store/layered_store_supplier.go index 841b75a32..482ccd126 100644 --- a/store/layered_store_supplier.go +++ b/store/layered_store_supplier.go @@ -31,4 +31,10 @@ type LayeredStoreSupplier interface { ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + + // Roles + RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult } diff --git a/store/local_cache_supplier.go b/store/local_cache_supplier.go index 3627c5b39..2343f10a7 100644 --- a/store/local_cache_supplier.go +++ b/store/local_cache_supplier.go @@ -13,7 +13,10 @@ import ( const ( REACTION_CACHE_SIZE = 20000 - REACTION_CACHE_SEC = 1800 // 30 minutes + REACTION_CACHE_SEC = 30 * 60 + + ROLE_CACHE_SIZE = 20000 + ROLE_CACHE_SEC = 30 * 60 CLEAR_CACHE_MESSAGE_DATA = "" ) @@ -21,6 +24,7 @@ const ( type LocalCacheSupplier struct { next LayeredStoreSupplier reactionCache *utils.Cache + roleCache *utils.Cache metrics einterfaces.MetricsInterface cluster einterfaces.ClusterInterface } @@ -28,12 +32,14 @@ type LocalCacheSupplier struct { func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) *LocalCacheSupplier { supplier := &LocalCacheSupplier{ reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS), + roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES), metrics: metrics, cluster: cluster, } if cluster != nil { cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, supplier.handleClusterInvalidateReaction) + cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, supplier.handleClusterInvalidateRole) } return supplier diff --git a/store/local_cache_supplier_roles.go b/store/local_cache_supplier_roles.go new file mode 100644 index 000000000..a9cbda017 --- /dev/null +++ b/store/local_cache_supplier_roles.go @@ -0,0 +1,68 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "context" + + "github.com/mattermost/mattermost-server/model" +) + +func (s *LocalCacheSupplier) handleClusterInvalidateRole(msg *model.ClusterMessage) { + if msg.Data == CLEAR_CACHE_MESSAGE_DATA { + s.roleCache.Purge() + } else { + s.roleCache.Remove(msg.Data) + } +} + +func (s *LocalCacheSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + if len(role.Id) != 0 { + defer s.doInvalidateCacheCluster(s.roleCache, role.Name) + } + return s.Next().RoleSave(ctx, role, hints...) +} + +func (s *LocalCacheSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + return s.Next().RoleGet(ctx, roleId, hints...) +} + +func (s *LocalCacheSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + if result := s.doStandardReadCache(ctx, s.roleCache, name, hints...); result != nil { + return result + } + + result := s.Next().RoleGetByName(ctx, name, hints...) + + s.doStandardAddToCache(ctx, s.roleCache, name, result, hints...) + + return result +} + +func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + var foundRoles []*model.Role + var rolesToQuery []string + + for _, roleName := range roleNames { + if result := s.doStandardReadCache(ctx, s.roleCache, roleName, hints...); result != nil { + foundRoles = append(foundRoles, result.Data.(*model.Role)) + } else { + rolesToQuery = append(rolesToQuery, roleName) + } + } + + result := s.Next().RoleGetByNames(ctx, rolesToQuery, hints...) + + if result.Data != nil { + rolesFound := result.Data.([]*model.Role) + for _, role := range rolesFound { + res := NewSupplierResult() + res.Data = role + s.doStandardAddToCache(ctx, s.roleCache, role.Name, res, hints...) + } + result.Data = append(foundRoles, result.Data.([]*model.Role)...) + } + + return result +} diff --git a/store/redis_supplier.go b/store/redis_supplier.go index 32dc12033..b8ec794cf 100644 --- a/store/redis_supplier.go +++ b/store/redis_supplier.go @@ -132,3 +132,23 @@ func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTim // Ignoring this. It's probably OK to have the emoji slowly expire from Redis. return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...) } + +func (s *RedisSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleSave(ctx, role, hints...) +} + +func (s *RedisSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGet(ctx, roleId, hints...) +} + +func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGetByName(ctx, name, hints...) +} + +func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGetByNames(ctx, roleNames, hints...) +} diff --git a/store/sqlstore/role_store_test.go b/store/sqlstore/role_store_test.go new file mode 100644 index 000000000..e89930f71 --- /dev/null +++ b/store/sqlstore/role_store_test.go @@ -0,0 +1,14 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sqlstore + +import ( + "testing" + + "github.com/mattermost/mattermost-server/store/storetest" +) + +func TestRoleStore(t *testing.T) { + StoreTest(t, storetest.TestRoleStore) +} diff --git a/store/sqlstore/role_supplier.go b/store/sqlstore/role_supplier.go new file mode 100644 index 000000000..41eed85e0 --- /dev/null +++ b/store/sqlstore/role_supplier.go @@ -0,0 +1,163 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sqlstore + +import ( + "context" + "database/sql" + "fmt" + "net/http" + "strings" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +type Role struct { + Id string + Name string + DisplayName string + Description string + Permissions string + SchemeManaged bool +} + +func NewRoleFromModel(role *model.Role) *Role { + permissionsMap := make(map[string]bool) + permissions := "" + + for _, permission := range role.Permissions { + if !permissionsMap[permission] { + permissions += fmt.Sprintf(" %v", permission) + permissionsMap[permission] = true + } + } + + return &Role{ + Id: role.Id, + Name: role.Name, + DisplayName: role.DisplayName, + Description: role.Description, + Permissions: permissions, + SchemeManaged: role.SchemeManaged, + } +} + +func (role Role) ToModel() *model.Role { + return &model.Role{ + Id: role.Id, + Name: role.Name, + DisplayName: role.DisplayName, + Description: role.Description, + Permissions: strings.Fields(role.Permissions), + SchemeManaged: role.SchemeManaged, + } +} + +func initSqlSupplierRoles(sqlStore SqlStore) { + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(Role{}, "Roles").SetKeys(false, "Id") + table.ColMap("Name").SetMaxSize(64).SetUnique(true) + table.ColMap("DisplayName").SetMaxSize(128) + table.ColMap("Description").SetMaxSize(1024) + table.ColMap("Permissions").SetMaxSize(4096) + } +} + +func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + // Check the role is valid before proceeding. + if !role.IsValidWithoutId() { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.invalid_role.app_error", nil, "", http.StatusBadRequest) + return result + } + + dbRole := NewRoleFromModel(role) + if len(dbRole.Id) == 0 { + dbRole.Id = model.NewId() + if err := s.GetMaster().Insert(dbRole); err != nil { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError) + } + } else { + if rowsChanged, err := s.GetMaster().Update(dbRole); err != nil { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, err.Error(), http.StatusInternalServerError) + } else if rowsChanged != 1 { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, "no record to update", http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRole Role + + if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil { + if err == sql.ErrNoRows { + result.Err = model.NewAppError("SqlRoleStore.Get", "store.sql_role.get.app_error", nil, "Id="+roleId+", "+err.Error(), http.StatusNotFound) + } else { + result.Err = model.NewAppError("SqlRoleStore.Get", "store.sql_role.get.app_error", nil, err.Error(), http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGetByName(ctx context.Context, name string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRole Role + + if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil { + if err == sql.ErrNoRows { + result.Err = model.NewAppError("SqlRoleStore.GetByName", "store.sql_role.get_by_name.app_error", nil, "name="+name+",err="+err.Error(), http.StatusNotFound) + } else { + result.Err = model.NewAppError("SqlRoleStore.GetByName", "store.sql_role.get_by_name.app_error", nil, "name="+name+",err="+err.Error(), http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRoles []*Role + + if len(names) == 0 { + result.Data = []*model.Role{} + return result + } + + var searchPlaceholders []string + var parameters = map[string]interface{}{} + for i, value := range names { + searchPlaceholders = append(searchPlaceholders, fmt.Sprintf(":Name%d", i)) + parameters[fmt.Sprintf("Name%d", i)] = value + } + + searchTerm := "Name IN (" + strings.Join(searchPlaceholders, ", ") + ")" + + if _, err := s.GetReplica().Select(&dbRoles, "SELECT * from Roles WHERE "+searchTerm, parameters); err != nil { + result.Err = model.NewAppError("SqlRoleStore.GetByNames", "store.sql_role.get_by_names.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + var roles []*model.Role + for _, dbRole := range dbRoles { + roles = append(roles, dbRole.ToModel()) + } + + result.Data = roles + + return result +} diff --git a/store/sqlstore/store.go b/store/sqlstore/store.go index cfdd7a552..1c623f0b1 100644 --- a/store/sqlstore/store.go +++ b/store/sqlstore/store.go @@ -87,4 +87,5 @@ type SqlStore interface { Job() store.JobStore Plugin() store.PluginStore UserAccessToken() store.UserAccessTokenStore + Role() store.RoleStore } diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go index 3b9528578..5e43ee0f0 100644 --- a/store/sqlstore/supplier.go +++ b/store/sqlstore/supplier.go @@ -86,6 +86,7 @@ type SqlSupplierOldStores struct { userAccessToken store.UserAccessTokenStore plugin store.PluginStore channelMemberHistory store.ChannelMemberHistoryStore + role store.RoleStore } type SqlSupplier struct { @@ -135,6 +136,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter supplier.oldStores.plugin = NewSqlPluginStore(supplier) initSqlSupplierReactions(supplier) + initSqlSupplierRoles(supplier) err := supplier.GetMaster().CreateTablesIfNotExists() if err != nil { @@ -811,6 +813,10 @@ func (ss *SqlSupplier) Plugin() store.PluginStore { return ss.oldStores.plugin } +func (ss *SqlSupplier) Role() store.RoleStore { + return ss.oldStores.role +} + func (ss *SqlSupplier) DropAllTables() { ss.master.TruncateTables() } diff --git a/store/store.go b/store/store.go index 2742c0889..40fd1e23b 100644 --- a/store/store.go +++ b/store/store.go @@ -61,6 +61,7 @@ type Store interface { Status() StatusStore FileInfo() FileInfoStore Reaction() ReactionStore + Role() RoleStore Job() JobStore UserAccessToken() UserAccessTokenStore ChannelMemberHistory() ChannelMemberHistoryStore @@ -461,3 +462,10 @@ type PluginStore interface { Get(pluginId, key string) StoreChannel Delete(pluginId, key string) StoreChannel } + +type RoleStore interface { + Save(role *model.Role) StoreChannel + Get(roleId string) StoreChannel + GetByName(name string) StoreChannel + GetByNames(names []string) StoreChannel +} diff --git a/store/storetest/mocks/LayeredStoreDatabaseLayer.go b/store/storetest/mocks/LayeredStoreDatabaseLayer.go index 9c66c4aac..d0162a01e 100644 --- a/store/storetest/mocks/LayeredStoreDatabaseLayer.go +++ b/store/storetest/mocks/LayeredStoreDatabaseLayer.go @@ -416,6 +416,114 @@ func (_m *LayeredStoreDatabaseLayer) ReactionSave(ctx context.Context, reaction return r0 } +// Role provides a mock function with given fields: +func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + +// RoleGet provides a mock function with given fields: ctx, roleId, hints +func (_m *LayeredStoreDatabaseLayer) RoleGet(ctx context.Context, roleId string, 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, roleId) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, roleId, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByName provides a mock function with given fields: ctx, name, hints +func (_m *LayeredStoreDatabaseLayer) RoleGetByName(ctx context.Context, name string, 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, name) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, name, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByNames provides a mock function with given fields: ctx, names, hints +func (_m *LayeredStoreDatabaseLayer) RoleGetByNames(ctx context.Context, names []string, 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, names) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, names, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleSave provides a mock function with given fields: ctx, role, hints +func (_m *LayeredStoreDatabaseLayer) RoleSave(ctx context.Context, role *model.Role, 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, role) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, *model.Role, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, role, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/mocks/LayeredStoreSupplier.go b/store/storetest/mocks/LayeredStoreSupplier.go index f4187dae9..59fd31cb8 100644 --- a/store/storetest/mocks/LayeredStoreSupplier.go +++ b/store/storetest/mocks/LayeredStoreSupplier.go @@ -145,6 +145,98 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode return r0 } +// RoleGet provides a mock function with given fields: ctx, roleId, hints +func (_m *LayeredStoreSupplier) RoleGet(ctx context.Context, roleId string, 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, roleId) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, roleId, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByName provides a mock function with given fields: ctx, name, hints +func (_m *LayeredStoreSupplier) RoleGetByName(ctx context.Context, name string, 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, name) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, name, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByNames provides a mock function with given fields: ctx, names, hints +func (_m *LayeredStoreSupplier) RoleGetByNames(ctx context.Context, names []string, 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, names) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, names, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleSave provides a mock function with given fields: ctx, role, hints +func (_m *LayeredStoreSupplier) RoleSave(ctx context.Context, role *model.Role, 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, role) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, *model.Role, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, role, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // SetChainNext provides a mock function with given fields: _a0 func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) { _m.Called(_a0) diff --git a/store/storetest/mocks/RoleStore.go b/store/storetest/mocks/RoleStore.go new file mode 100644 index 000000000..8150460ae --- /dev/null +++ b/store/storetest/mocks/RoleStore.go @@ -0,0 +1,78 @@ +// Code generated by mockery v1.0.0 + +// Regenerate this file using `make store-mocks`. + +package mocks + +import mock "github.com/stretchr/testify/mock" +import model "github.com/mattermost/mattermost-server/model" +import store "github.com/mattermost/mattermost-server/store" + +// RoleStore is an autogenerated mock type for the RoleStore type +type RoleStore struct { + mock.Mock +} + +// Get provides a mock function with given fields: roleId +func (_m *RoleStore) Get(roleId string) store.StoreChannel { + ret := _m.Called(roleId) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok { + r0 = rf(roleId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// GetByName provides a mock function with given fields: name +func (_m *RoleStore) GetByName(name string) store.StoreChannel { + ret := _m.Called(name) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// GetByNames provides a mock function with given fields: names +func (_m *RoleStore) GetByNames(names []string) store.StoreChannel { + ret := _m.Called(names) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func([]string) store.StoreChannel); ok { + r0 = rf(names) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// Save provides a mock function with given fields: role +func (_m *RoleStore) Save(role *model.Role) store.StoreChannel { + ret := _m.Called(role) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(*model.Role) store.StoreChannel); ok { + r0 = rf(role) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} diff --git a/store/storetest/mocks/SqlStore.go b/store/storetest/mocks/SqlStore.go index b9b962101..43709fc0e 100644 --- a/store/storetest/mocks/SqlStore.go +++ b/store/storetest/mocks/SqlStore.go @@ -538,6 +538,22 @@ func (_m *SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, return r0 } +// Role provides a mock function with given fields: +func (_m *SqlStore) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *SqlStore) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/mocks/Store.go b/store/storetest/mocks/Store.go index 40b50a554..cb7e511f6 100644 --- a/store/storetest/mocks/Store.go +++ b/store/storetest/mocks/Store.go @@ -283,6 +283,22 @@ func (_m *Store) Reaction() store.ReactionStore { return r0 } +// Role provides a mock function with given fields: +func (_m *Store) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *Store) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/role_store.go b/store/storetest/role_store.go new file mode 100644 index 000000000..499e36e1e --- /dev/null +++ b/store/storetest/role_store.go @@ -0,0 +1,244 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestRoleStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testRoleStoreSave(t, ss) }) + t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, ss) }) + t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, ss) }) + t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, ss) }) +} + +func testRoleStoreSave(t *testing.T, ss store.Store) { + // Save a new role. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + assert.Equal(t, r1.Name, d1.Name) + assert.Equal(t, r1.DisplayName, d1.DisplayName) + assert.Equal(t, r1.Description, d1.Description) + assert.Equal(t, r1.Permissions, d1.Permissions) + assert.Equal(t, r1.SchemeManaged, d1.SchemeManaged) + + // Change the role permissions and update. + d1.Permissions = []string{ + "invite_user", + "add_user_to_team", + "delete_public_channel", + } + + res2 := <-ss.Role().Save(d1) + assert.Nil(t, res2.Err) + d2 := res2.Data.(*model.Role) + assert.Len(t, d2.Id, 26) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, d1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Try saving one with an invalid ID set. + r3 := &model.Role{ + Id: model.NewId(), + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res3 := <-ss.Role().Save(r3) + assert.NotNil(t, res3.Err) + + // Try saving one with a duplicate "name" field. + r4 := &model.Role{ + Name: r1.Name, + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res4 := <-ss.Role().Save(r4) + assert.NotNil(t, res4.Err) +} + +func testRoleStoreGet(t *testing.T, ss store.Store) { + // Save a role to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + // Get a valid role + res2 := <-ss.Role().Get(d1.Id) + assert.Nil(t, res2.Err) + d2 := res1.Data.(*model.Role) + assert.Equal(t, d1.Id, d2.Id) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, r1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Get an invalid role + res3 := <-ss.Role().Get(model.NewId()) + assert.NotNil(t, res3.Err) +} + +func testRoleStoreGetByName(t *testing.T, ss store.Store) { + // Save a role to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + // Get a valid role + res2 := <-ss.Role().GetByName(d1.Name) + assert.Nil(t, res2.Err) + d2 := res1.Data.(*model.Role) + assert.Equal(t, d1.Id, d2.Id) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, r1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Get an invalid role + res3 := <-ss.Role().GetByName(model.NewId()) + assert.NotNil(t, res3.Err) +} + +func testRoleStoreGetByNames(t *testing.T, ss store.Store) { + // Save some roles to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + r2 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "read_channel", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + r3 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "delete_private_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + res2 := <-ss.Role().Save(r2) + assert.Nil(t, res2.Err) + d2 := res2.Data.(*model.Role) + assert.Len(t, d2.Id, 26) + + res3 := <-ss.Role().Save(r3) + assert.Nil(t, res3.Err) + d3 := res3.Data.(*model.Role) + assert.Len(t, d3.Id, 26) + + // Get two valid roles. + n4 := []string{r1.Name, r2.Name} + res4 := <-ss.Role().GetByNames(n4) + assert.Nil(t, res4.Err) + roles4 := res4.Data.([]*model.Role) + assert.Len(t, roles4, 2) + assert.Contains(t, roles4, d1) + assert.Contains(t, roles4, d2) + assert.NotContains(t, roles4, d3) + + // Get two invalid roles. + n5 := []string{model.NewId(), model.NewId()} + res5 := <-ss.Role().GetByNames(n5) + assert.Nil(t, res5.Err) + roles5 := res5.Data.([]*model.Role) + assert.Len(t, roles5, 0) + + // Get one valid one and one invalid one. + n6 := []string{r1.Name, model.NewId()} + res6 := <-ss.Role().GetByNames(n6) + assert.Nil(t, res6.Err) + roles6 := res6.Data.([]*model.Role) + assert.Len(t, roles6, 1) + assert.Contains(t, roles6, d1) + assert.NotContains(t, roles6, d2) + assert.NotContains(t, roles6, d3) +} diff --git a/store/storetest/store.go b/store/storetest/store.go index 367c5f441..44f426075 100644 --- a/store/storetest/store.go +++ b/store/storetest/store.go @@ -43,6 +43,7 @@ type Store struct { UserAccessTokenStore mocks.UserAccessTokenStore PluginStore mocks.PluginStore ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore + RoleStore mocks.RoleStore } func (s *Store) Team() store.TeamStore { return &s.TeamStore } @@ -68,6 +69,7 @@ func (s *Store) Reaction() store.ReactionStore { return &s.React func (s *Store) Job() store.JobStore { return &s.JobStore } func (s *Store) UserAccessToken() store.UserAccessTokenStore { return &s.UserAccessTokenStore } func (s *Store) Plugin() store.PluginStore { return &s.PluginStore } +func (s *Store) Role() store.RoleStore { return &s.RoleStore } func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore { return &s.ChannelMemberHistoryStore } @@ -104,5 +106,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool { &s.UserAccessTokenStore, &s.ChannelMemberHistoryStore, &s.PluginStore, + &s.RoleStore, ) } diff --git a/utils/authorization.go b/utils/authorization.go index 39a0d606c..b18ece141 100644 --- a/utils/authorization.go +++ b/utils/authorization.go @@ -7,14 +7,7 @@ import ( "github.com/mattermost/mattermost-server/model" ) -func DefaultRolesBasedOnConfig(cfg *model.Config) map[string]*model.Role { - roles := make(map[string]*model.Role) - for id, role := range model.DefaultRoles { - copy := &model.Role{} - *copy = *role - roles[id] = copy - } - +func SetRolePermissionsFromConfig(roles map[string]*model.Role, cfg *model.Config) map[string]*model.Role { if IsLicensed() { switch *cfg.TeamSettings.RestrictPublicChannelCreation { case model.PERMISSIONS_ALL: @@ -222,8 +215,8 @@ func DefaultRolesBasedOnConfig(cfg *model.Config) map[string]*model.Role { model.PERMISSION_ADD_USER_TO_TEAM.Id, ) } else if *cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_ALL { - roles[model.SYSTEM_USER_ROLE_ID].Permissions = append( - roles[model.SYSTEM_USER_ROLE_ID].Permissions, + roles[model.TEAM_USER_ROLE_ID].Permissions = append( + roles[model.TEAM_USER_ROLE_ID].Permissions, model.PERMISSION_INVITE_USER.Id, model.PERMISSION_ADD_USER_TO_TEAM.Id, ) @@ -243,11 +236,6 @@ func DefaultRolesBasedOnConfig(cfg *model.Config) map[string]*model.Role { roles[model.CHANNEL_USER_ROLE_ID].Permissions, model.PERMISSION_DELETE_POST.Id, ) - roles[model.CHANNEL_ADMIN_ROLE_ID].Permissions = append( - roles[model.CHANNEL_ADMIN_ROLE_ID].Permissions, - model.PERMISSION_DELETE_POST.Id, - model.PERMISSION_DELETE_OTHERS_POSTS.Id, - ) roles[model.TEAM_ADMIN_ROLE_ID].Permissions = append( roles[model.TEAM_ADMIN_ROLE_ID].Permissions, model.PERMISSION_DELETE_POST.Id, diff --git a/web/web_test.go b/web/web_test.go index 21a7968b3..f43251143 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -52,6 +52,8 @@ func Setup() *app.App { URL = fmt.Sprintf("http://localhost:%v", a.Srv.ListenAddr.Port) ApiClient = model.NewClient(URL) + a.DoAdvancedPermissionsMigration() + a.Srv.Store.MarkSystemRanUnitTests() a.UpdateConfig(func(cfg *model.Config) { -- cgit v1.2.3-1-g7c22