summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/apitestlib.go112
-rw-r--r--api/channel_test.go547
-rw-r--r--api/command_test.go3
-rw-r--r--api/oauth_test.go44
-rw-r--r--api/post_test.go62
-rw-r--r--api/reaction.go8
-rw-r--r--api/team.go14
-rw-r--r--api/team_test.go74
-rw-r--r--api/webhook_test.go122
-rw-r--r--api4/api.go5
-rw-r--r--api4/apitestlib.go112
-rw-r--r--api4/channel.go1
-rw-r--r--api4/channel_test.go347
-rw-r--r--api4/context.go23
-rw-r--r--api4/oauth_test.go85
-rw-r--r--api4/params.go10
-rw-r--r--api4/post_test.go3
-rw-r--r--api4/reaction.go12
-rw-r--r--api4/reaction_test.go464
-rw-r--r--api4/role.go133
-rw-r--r--api4/role_test.go214
-rw-r--r--api4/team_test.go88
-rw-r--r--api4/webhook.go28
-rw-r--r--api4/webhook_test.go72
-rw-r--r--app/app.go60
-rw-r--r--app/app_test.go345
-rw-r--r--app/apptestlib.go53
-rw-r--r--app/authorization.go56
-rw-r--r--app/authorization_test.go6
-rw-r--r--app/channel.go4
-rw-r--r--app/diagnostics.go2
-rw-r--r--app/import_test.go16
-rw-r--r--app/license.go1
-rw-r--r--app/post.go11
-rw-r--r--app/post_test.go37
-rw-r--r--app/role.go86
-rw-r--r--app/team.go4
-rw-r--r--app/user.go4
-rw-r--r--cmd/commands/server.go2
-rw-r--r--config/default.json2
-rw-r--r--i18n/en.json32
-rw-r--r--model/client4.go46
-rw-r--r--model/cluster_message.go1
-rw-r--r--model/config.go8
-rw-r--r--model/permission.go (renamed from model/authorization.go)335
-rw-r--r--model/permission_test.go18
-rw-r--r--model/role.go350
-rw-r--r--model/user.go7
-rw-r--r--model/user_test.go4
-rw-r--r--model/websocket_message.go1
-rw-r--r--store/layered_store.go34
-rw-r--r--store/layered_store_supplier.go6
-rw-r--r--store/local_cache_supplier.go8
-rw-r--r--store/local_cache_supplier_roles.go70
-rw-r--r--store/redis_supplier.go47
-rw-r--r--store/redis_supplier_reactions.go57
-rw-r--r--store/redis_supplier_roles.go89
-rw-r--r--store/sqlstore/role_store_test.go14
-rw-r--r--store/sqlstore/role_supplier.go175
-rw-r--r--store/sqlstore/store.go1
-rw-r--r--store/sqlstore/supplier.go6
-rw-r--r--store/sqlstore/upgrade.go4
-rw-r--r--store/store.go8
-rw-r--r--store/storetest/mocks/LayeredStoreDatabaseLayer.go108
-rw-r--r--store/storetest/mocks/LayeredStoreSupplier.go92
-rw-r--r--store/storetest/mocks/RoleStore.go78
-rw-r--r--store/storetest/mocks/SqlStore.go16
-rw-r--r--store/storetest/mocks/Store.go16
-rw-r--r--store/storetest/role_store.go244
-rw-r--r--store/storetest/store.go3
-rw-r--r--utils/authorization.go43
-rw-r--r--utils/authorization_test.go133
-rw-r--r--utils/config.go2
-rw-r--r--utils/policies-roles-mapping.json532
-rw-r--r--web/web_test.go2
75 files changed, 4360 insertions, 1502 deletions
diff --git a/api/apitestlib.go b/api/apitestlib.go
index 92a7c0cc8..699b0eb90 100644
--- a/api/apitestlib.go
+++ b/api/apitestlib.go
@@ -116,6 +116,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 })
@@ -395,3 +396,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 6d9eb5538..2642eb9ff 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -18,7 +18,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)
@@ -95,9 +94,14 @@ func TestCreateChannel(t *testing.T) {
t.Fatal("Should have errored out on direct channel type")
}
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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}
@@ -108,12 +112,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.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"
@@ -136,46 +138,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
- })
-
- 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.
- th.App.SetLicense(nil)
-
- 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 })
}
func TestCreateDirectChannel(t *testing.T) {
@@ -346,9 +308,17 @@ func TestUpdateChannel(t *testing.T) {
t.Fatal("should have failed - channel deleted")
}
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
@@ -367,10 +337,11 @@ func TestUpdateChannel(t *testing.T) {
t.Fatal(err)
}
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ 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)
th.App.Srv.Store.Channel().ClearCaches()
@@ -382,17 +353,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)
th.App.Srv.Store.Channel().ClearCaches()
@@ -403,62 +363,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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN
- })
- th.App.SetLicense(model.NewTestLicense())
-
- 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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- 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.
- th.App.SetLicense(nil)
-
- 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) {
@@ -494,7 +398,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}
@@ -564,9 +467,17 @@ func TestUpdateChannelHeader(t *testing.T) {
t.Fatal("should have errored non-channel member trying to update header")
}
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
@@ -589,10 +500,11 @@ func TestUpdateChannelHeader(t *testing.T) {
t.Fatal(err)
}
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ 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)
th.App.Srv.Store.Channel().ClearCaches()
@@ -614,64 +526,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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN
- })
-
- 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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- 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.
- th.App.SetLicense(nil)
-
- 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) {
@@ -679,7 +533,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}
@@ -735,9 +588,17 @@ func TestUpdateChannelPurpose(t *testing.T) {
t.Fatal("should have errored non-channel member trying to update purpose")
}
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
@@ -760,10 +621,11 @@ func TestUpdateChannelPurpose(t *testing.T) {
t.Fatal(err)
}
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ 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)
th.App.Srv.Store.Channel().ClearCaches()
@@ -785,63 +647,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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN
- })
-
- 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
- *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- 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.
- th.App.SetLicense(nil)
- 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) {
@@ -1296,9 +1101,14 @@ func TestDeleteChannel(t *testing.T) {
t.Fatal("should have failed - channel already deleted")
}
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
@@ -1320,10 +1130,10 @@ func TestDeleteChannel(t *testing.T) {
t.Fatal(err)
}
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ 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()
@@ -1356,67 +1166,6 @@ func TestDeleteChannel(t *testing.T) {
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()
-
- th.App.SetLicense(model.NewTestLicense())
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN
- })
-
- 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)
- }
-
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- 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))
@@ -1441,23 +1190,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.
- th.App.SetLicense(nil)
-
- 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)
- }
}
func TestGetChannelStats(t *testing.T) {
@@ -1533,21 +1265,14 @@ func TestAddChannelMember(t *testing.T) {
t.Fatal("Should have errored, user not on team")
}
- // Test policy does not apply to TE.
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- 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
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL })
- th.App.SetLicense(model.NewTestLicense())
+ 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}
@@ -1557,10 +1282,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
- })
+ 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)
@@ -1571,47 +1296,10 @@ func TestAddChannelMember(t *testing.T) {
th.MakeUserChannelAdmin(user1, channel5)
th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
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
- })
-
- 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()
- th.App.SetLicense(model.NewTestLicense())
-
- 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
- })
-
- 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) {
@@ -1683,22 +1371,14 @@ func TestRemoveChannelMember(t *testing.T) {
th.LoginBasic()
- // Test policy does not apply to TE.
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN
- })
-
- 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)
- }
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Add a license
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL })
- th.App.SetLicense(model.NewTestLicense())
+ 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}
@@ -1709,10 +1389,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
- })
+ 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)
@@ -1724,49 +1404,10 @@ func TestRemoveChannelMember(t *testing.T) {
th.MakeUserChannelAdmin(user1, channel5)
th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
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
- })
-
- 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()
- th.App.SetLicense(model.NewTestLicense())
-
- 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
- })
-
- 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/command_test.go b/api/command_test.go
index 7eadca124..7f9c6dcff 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -202,13 +202,10 @@ func TestDeleteCommand(t *testing.T) {
Client := th.SystemAdminClient
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
- onlyAdminIntegration := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = enableCommands })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = onlyAdminIntegration })
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"}
cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
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 2fc79d9b1..7a2367312 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -385,7 +385,14 @@ func TestUpdatePost(t *testing.T) {
Client := th.BasicClient
channel1 := th.BasicChannel
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS })
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+ th.App.SetLicense(model.NewTestLicense())
+
+ th.AddPermissionToRole(model.PERMISSION_EDIT_POST.Id, model.CHANNEL_USER_ROLE_ID)
post1 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"}
rpost1, err := Client.CreatePost(post1)
@@ -454,8 +461,7 @@ func TestUpdatePost(t *testing.T) {
}
// Test licensed policy controls for edit post
- th.App.SetLicense(model.NewTestLicense())
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_NEVER })
+ th.RemovePermissionFromRole(model.PERMISSION_EDIT_POST.Id, model.CHANNEL_USER_ROLE_ID)
post4 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
rpost4, err := Client.CreatePost(post4)
@@ -468,7 +474,7 @@ func TestUpdatePost(t *testing.T) {
t.Fatal("shouldn't have been able to update a message when not allowed")
}
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_TIME_LIMIT })
+ th.AddPermissionToRole(model.PERMISSION_EDIT_POST.Id, model.CHANNEL_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = 1 }) //seconds
post5 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
@@ -944,8 +950,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 })
-
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)
@@ -998,8 +1002,11 @@ func TestDeletePosts(t *testing.T) {
t.Fatal(err)
}
- // Test licensed policy controls for delete post
- th.App.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam)
@@ -1011,9 +1018,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.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()
@@ -1034,40 +1040,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.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.
- th.App.SetLicense(nil)
-
- 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/reaction.go b/api/reaction.go
index 991044cda..812c9f582 100644
--- a/api/reaction.go
+++ b/api/reaction.go
@@ -36,8 +36,8 @@ func saveReaction(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
- c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
+ if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_ADD_REACTION) {
+ c.SetPermissionError(model.PERMISSION_ADD_REACTION)
return
}
@@ -87,8 +87,8 @@ func deleteReaction(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_READ_CHANNEL) {
- c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
+ if !c.App.SessionHasPermissionToChannel(c.Session, channelId, model.PERMISSION_REMOVE_REACTION) {
+ c.SetPermissionError(model.PERMISSION_REMOVE_REACTION)
return
}
diff --git a/api/team.go b/api/team.go
index f590b8e8c..b1d8086d3 100644
--- a/api/team.go
+++ b/api/team.go
@@ -116,15 +116,13 @@ 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 c.App.License() != nil && !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"
- }
+ if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_INVITE_USER) {
+ c.SetPermissionError(model.PERMISSION_INVITE_USER)
+ return
+ }
- c.Err = model.NewAppError("inviteMembers", errorId, nil, "", http.StatusForbidden)
+ if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) {
+ c.SetPermissionError(model.PERMISSION_INVITE_USER)
return
}
diff --git a/api/team_test.go b/api/team_test.go
index 696cf31bb..3db454b62 100644
--- a/api/team_test.go
+++ b/api/team_test.go
@@ -139,17 +139,17 @@ func TestAddUserToTeam(t *testing.T) {
t.Fatal(err)
}
- // 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 })
-
- // 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)
- }
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Add an EE license.
- th.App.SetLicense(model.NewTestLicense())
+ // 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)
// Check that a regular user can't add someone to the team.
user4 := th.CreateUser(th.BasicClient)
@@ -160,28 +160,17 @@ func TestAddUserToTeam(t *testing.T) {
// Should work as team admin.
th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam)
th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN })
+
+ // 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 })
-
- // 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) {
@@ -515,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)
@@ -546,17 +534,21 @@ func TestInviteMembers(t *testing.T) {
t.Fatal("Should have errored out on no invites to send")
}
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN })
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // 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)
- }
-
- th.App.SetLicense(model.NewTestLicense())
-
if _, err := Client.InviteMembers(invites); err == nil {
t.Fatal("should have errored not team admin and licensed")
}
@@ -569,18 +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 })
-
- 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 f4d46496b..0b3073f83 100644
--- a/api/webhook_test.go
+++ b/api/webhook_test.go
@@ -24,8 +24,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}
@@ -91,7 +99,8 @@ func TestCreateIncomingHook(t *testing.T) {
t.Fatal(err)
}
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
+ // 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)
@@ -130,7 +139,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 })
+
+ 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)
@@ -213,7 +227,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 })
+
+ // 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}
@@ -235,7 +251,8 @@ func TestUpdateIncomingHook(t *testing.T) {
})
})
- 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)
Client.Logout()
th.UpdateUserToTeamAdmin(user2, team)
@@ -318,7 +335,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 })
+
+ 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)
@@ -344,7 +369,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 })
+ // 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)
@@ -368,7 +394,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 })
+
+ 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)
@@ -401,7 +435,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 })
+ // 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")
@@ -437,7 +472,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 })
+
+ 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"}}
@@ -507,7 +550,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 })
+ // 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)
@@ -539,7 +583,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 })
+
+ 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)
@@ -565,7 +617,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 })
+ // 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)
@@ -595,7 +648,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 })
+
+ 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)
@@ -668,14 +729,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 })
+ // 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 })
+ // Revoke permission from regular users.
+ th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID)
Client.Logout()
th.LinkUserToTeam(user3, team)
@@ -762,7 +826,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 })
+
+ 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)
@@ -795,7 +867,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 })
+ // 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")
@@ -829,7 +902,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 })
+
+ 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)
@@ -863,7 +944,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 })
+ // 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)
diff --git a/api4/api.go b/api4/api.go
index 871dca0ac..88526e4d3 100644
--- a/api4/api.go
+++ b/api4/api.go
@@ -98,6 +98,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_-\.]+}'
@@ -196,6 +198,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.BaseRoutes.Image = api.BaseRoutes.ApiRoot.PathPrefix("/image").Subrouter()
api.InitUser()
@@ -223,6 +227,7 @@ func Init(a *app.App, root *mux.Router, full bool) *API {
api.InitWebrtc()
api.InitOpenGraph()
api.InitPlugin()
+ api.InitRole()
api.InitImage()
root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(Handle404))
diff --git a/api4/apitestlib.go b/api4/apitestlib.go
index 386afdadd..4620c5f4e 100644
--- a/api4/apitestlib.go
+++ b/api4/apitestlib.go
@@ -122,6 +122,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 })
@@ -804,3 +805,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 d587ddc72..f5f6bd06d 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 427607dc5..4c27e040a 100644
--- a/api4/channel_test.go
+++ b/api4/channel_test.go
@@ -78,12 +78,16 @@ func TestCreateChannel(t *testing.T) {
_, resp = Client.CreateChannel(private)
CheckForbiddenStatus(t, resp)
- th.LoginBasic()
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
- // Check permissions with policy config changes
- 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.SetLicense(model.NewTestLicense())
+ th.LoginBasic()
channel.Name = GenerateTestChannelName()
_, resp = Client.CreateChannel(channel)
@@ -93,10 +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
- *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN
- })
+ 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)
@@ -122,46 +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
- *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- 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.
- th.App.SetLicense(nil)
-
- 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 {
@@ -805,12 +770,6 @@ func TestDeleteChannel(t *testing.T) {
_, resp = Client.DeleteChannel(publicChannel3.Id)
CheckNoError(t, resp)
- // successful delete by TeamAdmin of channel created by user
- publicChannel4 := th.CreatePublicChannel()
- th.LoginTeamAdmin()
- _, resp = Client.DeleteChannel(publicChannel4.Id)
- CheckNoError(t, resp)
-
// default channel cannot be deleted.
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, team.Id)
pass, resp = Client.DeleteChannel(defaultChannel.Id)
@@ -864,9 +823,14 @@ func TestDeleteChannel(t *testing.T) {
th.InitBasic().InitSystemAdmin()
- 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.SetLicense(model.NewTestLicense())
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
Client = th.Client
team = th.BasicTeam
@@ -887,10 +851,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
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN
- })
+ // Restrict permissions to Channel Admins
+ 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)
// channels created by SystemAdmin
publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN)
@@ -917,115 +882,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()
- th.App.SetLicense(model.NewTestLicense())
-
- _, 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
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN
- })
- th.UpdateUserToNonTeamAdmin(user, team)
- th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
-
- // 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)
- th.App.Srv.Store.Channel().ClearCaches()
-
- _, 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()
- th.App.SetLicense(model.NewTestLicense())
-
- _, 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
- *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN
- })
-
- // 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)
- th.App.Srv.Store.Channel().ClearCaches()
-
- _, 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()
- th.App.SetLicense(model.NewTestLicense())
-
- _, 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)
@@ -1768,25 +1627,13 @@ func TestAddChannelMember(t *testing.T) {
_, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id)
CheckNoError(t, resp)
- // Test policy does not apply to TE.
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN
- })
-
- 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()
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Add a license
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL })
- th.App.SetLicense(model.NewTestLicense())
+ 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)
@@ -1800,10 +1647,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
- })
+ // 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()
@@ -1818,56 +1664,11 @@ func TestAddChannelMember(t *testing.T) {
th.MakeUserChannelAdmin(user, privateChannel)
th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
-
- 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
- })
-
- 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()
- th.App.SetLicense(model.NewTestLicense())
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
- })
-
- 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) {
@@ -1929,26 +1730,16 @@ func TestRemoveChannelMember(t *testing.T) {
th.UpdateUserToNonTeamAdmin(user1, team)
th.App.InvalidateAllCaches()
- // Test policy does not apply to TE.
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN
- })
-
- 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)
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Add a license
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL })
- th.App.SetLicense(model.NewTestLicense())
+ 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)
@@ -1957,10 +1748,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
- })
+ // 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)
@@ -1973,47 +1763,8 @@ func TestRemoveChannelMember(t *testing.T) {
th.MakeUserChannelAdmin(user1, privateChannel)
th.App.InvalidateAllCaches()
- th.App.SetLicense(model.NewTestLicense())
-
- _, 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
- })
-
- 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()
- th.App.SetLicense(model.NewTestLicense())
-
- _, 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
- })
-
- 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 df249f8de..62fe55758 100644
--- a/api4/context.go
+++ b/api4/context.go
@@ -636,3 +636,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 c871dafff..0862f13f5 100644
--- a/api4/oauth_test.go
+++ b/api4/oauth_test.go
@@ -18,6 +18,14 @@ func TestCreateOAuthApp(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
+
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}, IsTrusted: true}
@@ -34,11 +42,15 @@ func TestCreateOAuthApp(t *testing.T) {
t.Fatal("trusted did no match")
}
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true })
+ // 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 })
+ // 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)
@@ -77,6 +89,13 @@ func TestUpdateOAuthApp(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
oapp := &model.OAuthApp{
@@ -155,7 +174,9 @@ func TestUpdateOAuthApp(t *testing.T) {
th.LoginBasic()
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
+ // Revoke permission from regular users.
+ th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
+
_, resp = Client.UpdateOAuthApp(oapp)
CheckForbiddenStatus(t, resp)
@@ -164,6 +185,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)
@@ -182,8 +204,14 @@ func TestGetOAuthApps(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
@@ -226,7 +254,8 @@ func TestGetOAuthApps(t *testing.T) {
t.Fatal("wrong apps returned")
}
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true })
+ // 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)
@@ -247,8 +276,14 @@ func TestGetOAuthApp(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
@@ -287,7 +322,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 })
+ // 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)
@@ -314,8 +350,14 @@ func TestGetOAuthAppInfo(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
@@ -354,7 +396,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 })
+ // 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)
@@ -381,8 +424,14 @@ func TestDeleteOAuthApp(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
@@ -416,7 +465,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 })
+ // 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)
@@ -441,8 +492,14 @@ func TestRegenerateOAuthAppSecret(t *testing.T) {
Client := th.Client
AdminClient := th.SystemAdminClient
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ // Grant permission to regular users.
+ th.AddPermissionToRole(model.PERMISSION_MANAGE_OAUTH.Id, model.SYSTEM_USER_ROLE_ID)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false })
oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
@@ -480,7 +537,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 })
+ // 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)
diff --git a/api4/params.go b/api4/params.go
index 070efbbc6..e8e3f25e7 100644
--- a/api4/params.go
+++ b/api4/params.go
@@ -45,6 +45,8 @@ type ApiParams struct {
JobId string
JobType string
ActionId string
+ RoleId string
+ RoleName string
Page int
PerPage int
LogsPerPage int
@@ -157,6 +159,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(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 257918525..1b682e38b 100644
--- a/api4/post_test.go
+++ b/api4/post_test.go
@@ -130,7 +130,6 @@ func testCreatePostWithOutgoingHook(
channel := th.BasicChannel
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true })
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
})
@@ -477,7 +476,6 @@ func TestUpdatePost(t *testing.T) {
channel := th.BasicChannel
th.App.SetLicense(model.NewTestLicense())
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS })
post := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a"}
rpost, resp := Client.CreatePost(post)
@@ -549,7 +547,6 @@ func TestPatchPost(t *testing.T) {
channel := th.BasicChannel
th.App.SetLicense(model.NewTestLicense())
- th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS })
post := &model.Post{
ChannelId: channel.Id,
diff --git a/api4/reaction.go b/api4/reaction.go
index af637bf91..337b49751 100644
--- a/api4/reaction.go
+++ b/api4/reaction.go
@@ -32,8 +32,8 @@ func saveReaction(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !c.App.SessionHasPermissionToChannelByPost(c.Session, reaction.PostId, model.PERMISSION_READ_CHANNEL) {
- c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
+ if !c.App.SessionHasPermissionToChannelByPost(c.Session, reaction.PostId, model.PERMISSION_ADD_REACTION) {
+ c.SetPermissionError(model.PERMISSION_ADD_REACTION)
return
}
@@ -82,13 +82,13 @@ func deleteReaction(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
- c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
+ if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_REMOVE_REACTION) {
+ c.SetPermissionError(model.PERMISSION_REMOVE_REACTION)
return
}
- if c.Params.UserId != c.Session.UserId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
- c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
+ if c.Params.UserId != c.Session.UserId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_REMOVE_OTHERS_REACTIONS) {
+ c.SetPermissionError(model.PERMISSION_REMOVE_OTHERS_REACTIONS)
return
}
diff --git a/api4/reaction_test.go b/api4/reaction_test.go
index 93cd754c9..ac1a49671 100644
--- a/api4/reaction_test.go
+++ b/api4/reaction_test.go
@@ -19,116 +19,159 @@ func TestSaveReaction(t *testing.T) {
userId := th.BasicUser.Id
postId := th.BasicPost.Id
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
reaction := &model.Reaction{
UserId: userId,
PostId: postId,
EmojiName: "smile",
}
- rr, resp := Client.SaveReaction(reaction)
- CheckNoError(t, resp)
+ t.Run("successful-reaction", func(t *testing.T) {
+ rr, resp := Client.SaveReaction(reaction)
+ CheckNoError(t, resp)
- if rr.UserId != reaction.UserId {
- t.Fatal("UserId did not match")
- }
+ if rr.UserId != reaction.UserId {
+ t.Fatal("UserId did not match")
+ }
- if rr.PostId != reaction.PostId {
- t.Fatal("PostId did not match")
- }
+ if rr.PostId != reaction.PostId {
+ t.Fatal("PostId did not match")
+ }
- if rr.EmojiName != reaction.EmojiName {
- t.Fatal("EmojiName did not match")
- }
+ if rr.EmojiName != reaction.EmojiName {
+ t.Fatal("EmojiName did not match")
+ }
- if rr.CreateAt == 0 {
- t.Fatal("CreateAt should exist")
- }
+ if rr.CreateAt == 0 {
+ t.Fatal("CreateAt should exist")
+ }
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 1 {
- t.Fatal("didn't save reaction correctly")
- }
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 1 {
+ t.Fatal("didn't save reaction correctly")
+ }
+ })
- // saving a duplicate reaction
- rr, resp = Client.SaveReaction(reaction)
- CheckNoError(t, resp)
+ t.Run("duplicated-reaction", func(t *testing.T) {
+ _, resp := Client.SaveReaction(reaction)
+ CheckNoError(t, resp)
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 1 {
- t.Fatal("should have not save duplicated reaction")
- }
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 1 {
+ t.Fatal("should have not save duplicated reaction")
+ }
+ })
- reaction.EmojiName = "sad"
+ t.Run("save-second-reaction", func(t *testing.T) {
+ reaction.EmojiName = "sad"
- rr, resp = Client.SaveReaction(reaction)
- CheckNoError(t, resp)
+ rr, resp := Client.SaveReaction(reaction)
+ CheckNoError(t, resp)
- if rr.EmojiName != reaction.EmojiName {
- t.Fatal("EmojiName did not match")
- }
+ if rr.EmojiName != reaction.EmojiName {
+ t.Fatal("EmojiName did not match")
+ }
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 2 {
- t.Fatal("should have save multiple reactions")
- }
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 2 {
+ t.Fatal("should have save multiple reactions")
+ }
+ })
- // saving special case
- reaction.EmojiName = "+1"
+ t.Run("saving-special-case", func(t *testing.T) {
+ reaction.EmojiName = "+1"
- rr, resp = Client.SaveReaction(reaction)
- CheckNoError(t, resp)
+ rr, resp := Client.SaveReaction(reaction)
+ CheckNoError(t, resp)
- if rr.EmojiName != reaction.EmojiName {
- t.Fatal("EmojiName did not match")
- }
+ if rr.EmojiName != reaction.EmojiName {
+ t.Fatal("EmojiName did not match")
+ }
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 3 {
- t.Fatal("should have save multiple reactions")
- }
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil && len(reactions) != 3 {
+ t.Fatal("should have save multiple reactions")
+ }
+ })
+
+ t.Run("react-to-not-existing-post-id", func(t *testing.T) {
+ reaction.PostId = GenerateTestId()
+
+ _, resp := Client.SaveReaction(reaction)
+ CheckForbiddenStatus(t, resp)
+ })
- reaction.PostId = GenerateTestId()
+ t.Run("react-to-not-valid-post-id", func(t *testing.T) {
+ reaction.PostId = "junk"
- _, resp = Client.SaveReaction(reaction)
- CheckForbiddenStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckBadRequestStatus(t, resp)
+ })
- reaction.PostId = "junk"
+ t.Run("react-as-not-existing-user-id", func(t *testing.T) {
+ reaction.PostId = postId
+ reaction.UserId = GenerateTestId()
- _, resp = Client.SaveReaction(reaction)
- CheckBadRequestStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckForbiddenStatus(t, resp)
+ })
- reaction.PostId = postId
- reaction.UserId = GenerateTestId()
+ t.Run("react-as-not-valid-user-id", func(t *testing.T) {
+ reaction.UserId = "junk"
- _, resp = Client.SaveReaction(reaction)
- CheckForbiddenStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckBadRequestStatus(t, resp)
+ })
- reaction.UserId = "junk"
+ t.Run("react-as-empty-emoji-name", func(t *testing.T) {
+ reaction.UserId = userId
+ reaction.EmojiName = ""
- _, resp = Client.SaveReaction(reaction)
- CheckBadRequestStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckBadRequestStatus(t, resp)
+ })
- reaction.UserId = userId
- reaction.EmojiName = ""
+ t.Run("react-as-not-valid-emoji-name", func(t *testing.T) {
+ reaction.EmojiName = strings.Repeat("a", 65)
- _, resp = Client.SaveReaction(reaction)
- CheckBadRequestStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckBadRequestStatus(t, resp)
+ })
- reaction.EmojiName = strings.Repeat("a", 65)
+ t.Run("react-as-other-user", func(t *testing.T) {
+ reaction.EmojiName = "smile"
+ otherUser := th.CreateUser()
+ Client.Logout()
+ Client.Login(otherUser.Email, otherUser.Password)
- _, resp = Client.SaveReaction(reaction)
- CheckBadRequestStatus(t, resp)
+ _, resp := Client.SaveReaction(reaction)
+ CheckForbiddenStatus(t, resp)
+ })
- reaction.EmojiName = "smile"
- otherUser := th.CreateUser()
- Client.Logout()
- Client.Login(otherUser.Email, otherUser.Password)
+ t.Run("react-being-not-logged-in", func(t *testing.T) {
+ Client.Logout()
+ _, resp := Client.SaveReaction(reaction)
+ CheckUnauthorizedStatus(t, resp)
+ })
- _, resp = Client.SaveReaction(reaction)
- CheckForbiddenStatus(t, resp)
+ t.Run("react-as-other-user-being-system-admin", func(t *testing.T) {
+ _, resp := th.SystemAdminClient.SaveReaction(reaction)
+ CheckForbiddenStatus(t, resp)
+ })
- Client.Logout()
- _, resp = Client.SaveReaction(reaction)
- CheckUnauthorizedStatus(t, resp)
+ t.Run("unable-to-create-reaction-without-permissions", func(t *testing.T) {
+ th.LoginBasic()
- _, resp = th.SystemAdminClient.SaveReaction(reaction)
- CheckForbiddenStatus(t, resp)
+ th.RemovePermissionFromRole(model.PERMISSION_ADD_REACTION.Id, model.CHANNEL_USER_ROLE_ID)
+ _, resp := Client.SaveReaction(reaction)
+ CheckForbiddenStatus(t, resp)
+
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 3 {
+ t.Fatal("should have not created a reactions")
+ }
+ th.AddPermissionToRole(model.PERMISSION_ADD_REACTION.Id, model.CHANNEL_USER_ROLE_ID)
+ })
}
func TestGetReactions(t *testing.T) {
@@ -177,29 +220,39 @@ func TestGetReactions(t *testing.T) {
}
}
- rr, resp := Client.GetReactions(postId)
- CheckNoError(t, resp)
+ t.Run("get-reactions", func(t *testing.T) {
+ rr, resp := Client.GetReactions(postId)
+ CheckNoError(t, resp)
- assert.Len(t, rr, 5)
- for _, r := range reactions {
- assert.Contains(t, reactions, r)
- }
+ assert.Len(t, rr, 5)
+ for _, r := range reactions {
+ assert.Contains(t, reactions, r)
+ }
+ })
- rr, resp = Client.GetReactions("junk")
- CheckBadRequestStatus(t, resp)
+ t.Run("get-reactions-of-invalid-post-id", func(t *testing.T) {
+ rr, resp := Client.GetReactions("junk")
+ CheckBadRequestStatus(t, resp)
- assert.Empty(t, rr)
+ assert.Empty(t, rr)
+ })
- _, resp = Client.GetReactions(GenerateTestId())
- CheckForbiddenStatus(t, resp)
+ t.Run("get-reactions-of-not-existing-post-id", func(t *testing.T) {
+ _, resp := Client.GetReactions(GenerateTestId())
+ CheckForbiddenStatus(t, resp)
+ })
- Client.Logout()
+ t.Run("get-reactions-as-anonymous-user", func(t *testing.T) {
+ Client.Logout()
- _, resp = Client.GetReactions(postId)
- CheckUnauthorizedStatus(t, resp)
+ _, resp := Client.GetReactions(postId)
+ CheckUnauthorizedStatus(t, resp)
+ })
- _, resp = th.SystemAdminClient.GetReactions(postId)
- CheckNoError(t, resp)
+ t.Run("get-reactions-as-system-admin", func(t *testing.T) {
+ _, resp := th.SystemAdminClient.GetReactions(postId)
+ CheckNoError(t, resp)
+ })
}
func TestDeleteReaction(t *testing.T) {
@@ -216,131 +269,186 @@ func TestDeleteReaction(t *testing.T) {
EmojiName: "smile",
}
- th.App.SaveReactionForPost(r1)
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 {
- t.Fatal("didn't save reaction correctly")
- }
-
- ok, resp := Client.DeleteReaction(r1)
- CheckNoError(t, resp)
-
- if !ok {
- t.Fatal("should have returned true")
- }
-
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 0 {
- t.Fatal("should have deleted reaction")
- }
-
- // deleting one reaction when a post has multiple reactions
r2 := &model.Reaction{
UserId: userId,
PostId: postId,
EmojiName: "smile-",
}
- th.App.SaveReactionForPost(r1)
- th.App.SaveReactionForPost(r2)
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
- t.Fatal("didn't save reactions correctly")
- }
-
- _, resp = Client.DeleteReaction(r2)
- CheckNoError(t, resp)
-
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 || *reactions[0] != *r1 {
- t.Fatal("should have deleted 1 reaction only")
- }
-
- // deleting one reaction of name +1
r3 := &model.Reaction{
UserId: userId,
PostId: postId,
EmojiName: "+1",
}
- th.App.SaveReactionForPost(r3)
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
- t.Fatal("didn't save reactions correctly")
- }
-
- _, resp = Client.DeleteReaction(r3)
- CheckNoError(t, resp)
-
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 || *reactions[0] != *r1 {
- t.Fatal("should have deleted 1 reaction only")
- }
-
- // deleting a reaction made by another user
r4 := &model.Reaction{
UserId: user2Id,
PostId: postId,
EmojiName: "smile_",
}
- th.LoginBasic2()
- th.App.SaveReactionForPost(r4)
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
- t.Fatal("didn't save reaction correctly")
- }
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- th.LoginBasic()
+ t.Run("delete-reaction", func(t *testing.T) {
+ th.App.SaveReactionForPost(r1)
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 {
+ t.Fatal("didn't save reaction correctly")
+ }
- ok, resp = Client.DeleteReaction(r4)
- CheckForbiddenStatus(t, resp)
+ ok, resp := Client.DeleteReaction(r1)
+ CheckNoError(t, resp)
- if ok {
- t.Fatal("should have returned false")
- }
+ if !ok {
+ t.Fatal("should have returned true")
+ }
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
- t.Fatal("should have not deleted a reaction")
- }
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 0 {
+ t.Fatal("should have deleted reaction")
+ }
+ })
- r1.PostId = GenerateTestId()
- _, resp = Client.DeleteReaction(r1)
- CheckForbiddenStatus(t, resp)
+ t.Run("delete-reaction-when-post-has-multiple-reactions", func(t *testing.T) {
+ th.App.SaveReactionForPost(r1)
+ th.App.SaveReactionForPost(r2)
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
+ t.Fatal("didn't save reactions correctly")
+ }
+
+ _, resp := Client.DeleteReaction(r2)
+ CheckNoError(t, resp)
- r1.PostId = "junk"
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 || *reactions[0] != *r1 {
+ t.Fatal("should have deleted 1 reaction only")
+ }
+ })
+
+ t.Run("delete-reaction-when-plus-one-reaction-name", func(t *testing.T) {
+ th.App.SaveReactionForPost(r3)
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
+ t.Fatal("didn't save reactions correctly")
+ }
- _, resp = Client.DeleteReaction(r1)
- CheckBadRequestStatus(t, resp)
+ _, resp := Client.DeleteReaction(r3)
+ CheckNoError(t, resp)
- r1.PostId = postId
- r1.UserId = GenerateTestId()
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 || *reactions[0] != *r1 {
+ t.Fatal("should have deleted 1 reaction only")
+ }
+ })
- _, resp = Client.DeleteReaction(r1)
- CheckForbiddenStatus(t, resp)
+ t.Run("delete-reaction-made-by-another-user", func(t *testing.T) {
+ th.LoginBasic2()
+ th.App.SaveReactionForPost(r4)
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
+ t.Fatal("didn't save reaction correctly")
+ }
- r1.UserId = "junk"
+ th.LoginBasic()
- _, resp = Client.DeleteReaction(r1)
- CheckBadRequestStatus(t, resp)
+ ok, resp := Client.DeleteReaction(r4)
+ CheckForbiddenStatus(t, resp)
- r1.UserId = userId
- r1.EmojiName = ""
+ if ok {
+ t.Fatal("should have returned false")
+ }
- _, resp = Client.DeleteReaction(r1)
- CheckNotFoundStatus(t, resp)
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 2 {
+ t.Fatal("should have not deleted a reaction")
+ }
+ })
- r1.EmojiName = strings.Repeat("a", 65)
+ t.Run("delete-reaction-from-not-existing-post-id", func(t *testing.T) {
+ r1.PostId = GenerateTestId()
+ _, resp := Client.DeleteReaction(r1)
+ CheckForbiddenStatus(t, resp)
+ })
- _, resp = Client.DeleteReaction(r1)
- CheckBadRequestStatus(t, resp)
+ t.Run("delete-reaction-from-not-valid-post-id", func(t *testing.T) {
+ r1.PostId = "junk"
- Client.Logout()
- r1.EmojiName = "smile"
+ _, resp := Client.DeleteReaction(r1)
+ CheckBadRequestStatus(t, resp)
+ })
- _, resp = Client.DeleteReaction(r1)
- CheckUnauthorizedStatus(t, resp)
+ t.Run("delete-reaction-from-not-existing-user-id", func(t *testing.T) {
+ r1.PostId = postId
+ r1.UserId = GenerateTestId()
- _, resp = th.SystemAdminClient.DeleteReaction(r1)
- CheckNoError(t, resp)
+ _, resp := Client.DeleteReaction(r1)
+ CheckForbiddenStatus(t, resp)
+ })
- _, resp = th.SystemAdminClient.DeleteReaction(r4)
- CheckNoError(t, resp)
+ t.Run("delete-reaction-from-not-valid-user-id", func(t *testing.T) {
+ r1.UserId = "junk"
- if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 0 {
- t.Fatal("should have deleted both reactions")
- }
+ _, resp := Client.DeleteReaction(r1)
+ CheckBadRequestStatus(t, resp)
+ })
+
+ t.Run("delete-reaction-with-empty-name", func(t *testing.T) {
+ r1.UserId = userId
+ r1.EmojiName = ""
+
+ _, resp := Client.DeleteReaction(r1)
+ CheckNotFoundStatus(t, resp)
+ })
+
+ t.Run("delete-reaction-with-not-existing-name", func(t *testing.T) {
+ r1.EmojiName = strings.Repeat("a", 65)
+
+ _, resp := Client.DeleteReaction(r1)
+ CheckBadRequestStatus(t, resp)
+ })
+
+ t.Run("delete-reaction-as-anonymous-user", func(t *testing.T) {
+ Client.Logout()
+ r1.EmojiName = "smile"
+
+ _, resp := Client.DeleteReaction(r1)
+ CheckUnauthorizedStatus(t, resp)
+ })
+
+ t.Run("delete-reaction-as-system-admin", func(t *testing.T) {
+ _, resp := th.SystemAdminClient.DeleteReaction(r1)
+ CheckNoError(t, resp)
+
+ _, resp = th.SystemAdminClient.DeleteReaction(r4)
+ CheckNoError(t, resp)
+
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 0 {
+ t.Fatal("should have deleted both reactions")
+ }
+ })
+
+ t.Run("unable-to-delete-reaction-without-permissions", func(t *testing.T) {
+ th.LoginBasic()
+
+ th.RemovePermissionFromRole(model.PERMISSION_REMOVE_REACTION.Id, model.CHANNEL_USER_ROLE_ID)
+ th.App.SaveReactionForPost(r1)
+
+ _, resp := Client.DeleteReaction(r1)
+ CheckForbiddenStatus(t, resp)
+
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 {
+ t.Fatal("should have not deleted a reactions")
+ }
+ th.AddPermissionToRole(model.PERMISSION_REMOVE_REACTION.Id, model.CHANNEL_USER_ROLE_ID)
+ })
+
+ t.Run("unable-to-delete-others-reactions-without-permissions", func(t *testing.T) {
+ th.RemovePermissionFromRole(model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id, model.SYSTEM_ADMIN_ROLE_ID)
+ th.App.SaveReactionForPost(r1)
+
+ _, resp := th.SystemAdminClient.DeleteReaction(r1)
+ CheckForbiddenStatus(t, resp)
+
+ if reactions, err := th.App.GetReactionsForPost(postId); err != nil || len(reactions) != 1 {
+ t.Fatal("should have not deleted a reactions")
+ }
+ th.AddPermissionToRole(model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id, model.SYSTEM_ADMIN_ROLE_ID)
+ })
}
diff --git a/api4/role.go b/api4/role.go
new file mode 100644
index 000000000..c4203137b
--- /dev/null
+++ b/api4/role.go
@@ -0,0 +1,133 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api4
+
+import (
+ "net/http"
+ "strings"
+
+ "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
+ }
+
+ var cleanedRoleNames []string
+ for _, rolename := range rolenames {
+ if strings.TrimSpace(rolename) == "" {
+ continue
+ }
+
+ if !model.IsValidRoleName(rolename) {
+ c.SetInvalidParam("rolename")
+ return
+ }
+
+ cleanedRoleNames = append(cleanedRoleNames, rolename)
+ }
+
+ if roles, err := c.App.GetRolesByNames(cleanedRoleNames); 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.License() == nil && patch.Permissions != nil {
+ allowedPermissions := []string{
+ model.PERMISSION_CREATE_TEAM.Id,
+ model.PERMISSION_MANAGE_WEBHOOKS.Id,
+ model.PERMISSION_MANAGE_SLASH_COMMANDS.Id,
+ model.PERMISSION_MANAGE_OAUTH.Id,
+ model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
+ }
+
+ changedPermissions := model.PermissionsChangedByPatch(oldRole, patch)
+ for _, permission := range changedPermissions {
+ allowed := false
+ for _, allowedPermission := range allowedPermissions {
+ if permission == allowedPermission {
+ allowed = true
+ }
+ }
+
+ if !allowed {
+ c.Err = model.NewAppError("Api4.PatchRoles", "api.roles.patch_roles.license.error", nil, "", http.StatusNotImplemented)
+ 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..c5d8e303e
--- /dev/null
+++ b/api4/role_test.go
@@ -0,0 +1,214 @@
+// 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 non-existant roles.
+ received, resp = th.Client.GetRolesByNames([]string{model.NewId(), model.NewId()})
+ CheckNoError(t, resp)
+
+ // Empty list should error.
+ _, resp = th.SystemAdminClient.GetRolesByNames([]string{})
+ CheckBadRequestStatus(t, resp)
+
+ // Invalid role name should error.
+ received, resp = th.Client.GetRolesByNames([]string{model.NewId(), model.NewId(), "!!!!!!"})
+ CheckBadRequestStatus(t, resp)
+
+ // Empty/whitespace rolenames should be ignored.
+ received, resp = th.Client.GetRolesByNames([]string{model.NewId(), model.NewId(), "", " "})
+ CheckNoError(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", "manage_slash_commands"},
+ 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", "create_public_channel", "manage_webhooks"},
+ }
+
+ 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", "create_public_channel", "manage_webhooks"})
+ 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)
+
+ // Check a change that the license would not allow.
+ patch = &model.RolePatch{
+ Permissions: &[]string{"manage_system", "manage_webhooks"},
+ }
+
+ received, resp = th.SystemAdminClient.PatchRole(role.Id, patch)
+ CheckNotImplementedStatus(t, resp)
+
+ // Add a license.
+ th.App.SetLicense(model.NewTestLicense())
+
+ // Try again, should succeed
+ 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", "manage_webhooks"})
+ assert.Equal(t, received.SchemeManaged, role.SchemeManaged)
+}
diff --git a/api4/team_test.go b/api4/team_test.go
index c2edcdaaa..f969d05b2 100644
--- a/api4/team_test.go
+++ b/api4/team_test.go
@@ -69,8 +69,14 @@ func TestCreateTeam(t *testing.T) {
_, resp = Client.CreateTeam(rteam)
CheckUnauthorizedStatus(t, resp)
- // Update permission
- th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableTeamCreation = false })
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
+
+ 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)
@@ -1314,16 +1320,18 @@ func TestAddTeamMember(t *testing.T) {
Client.Logout()
- // 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.LoginBasic()
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Test without the EE license to see that the permission restriction is ignored.
- _, resp = Client.AddTeamMember(team.Id, otherUser.Id)
- CheckNoError(t, resp)
+ // 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)
- // Add an EE license.
- th.App.SetLicense(model.NewTestLicense())
th.LoginBasic()
// Check that a regular user can't add someone to the team.
@@ -1333,38 +1341,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 })
- th.App.SetLicense(model.NewTestLicense())
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 })
+ // 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 })
- th.App.SetLicense(model.NewTestLicense())
th.LoginBasic()
// Should work as a regular user.
_, resp = Client.AddTeamMember(team.Id, otherUser.Id)
CheckNoError(t, resp)
- th.LoginBasic()
-
// by hash and data
Client.Login(otherUser.Email, otherUser.Password)
@@ -1502,16 +1498,18 @@ func TestAddTeamMembers(t *testing.T) {
Client.Logout()
- // 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.LoginBasic()
+ // Check the appropriate permissions are enforced.
+ defaultRolePermissions := th.SaveDefaultRolePermissions()
+ defer func() {
+ th.RestoreDefaultRolePermissions(defaultRolePermissions)
+ }()
- // Test without the EE license to see that the permission restriction is ignored.
- _, resp = Client.AddTeamMembers(team.Id, userList)
- CheckNoError(t, resp)
+ // 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)
- // Add an EE license.
- th.App.SetLicense(model.NewTestLicense())
th.LoginBasic()
// Check that a regular user can't add someone to the team.
@@ -1521,30 +1519,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 })
- th.App.SetLicense(model.NewTestLicense())
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 })
-
- // 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 })
- th.App.SetLicense(model.NewTestLicense())
th.LoginBasic()
// Should work as a regular user.
diff --git a/api4/webhook.go b/api4/webhook.go
index 853cf43f3..52c4ea331 100644
--- a/api4/webhook.go
+++ b/api4/webhook.go
@@ -194,10 +194,16 @@ func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("fail - bad permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
return
- } else {
- w.Write([]byte(hook.ToJson()))
+ }
+
+ if c.Session.UserId != hook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
+ c.LogAudit("fail - inappropriate permissions")
+ c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
return
}
+
+ w.Write([]byte(hook.ToJson()))
+ return
}
}
@@ -228,14 +234,20 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("fail - bad permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_WEBHOOKS)
return
- } else {
- if err = c.App.DeleteIncomingWebhook(hookId); err != nil {
- c.Err = err
- return
- }
+ }
- ReturnStatusOK(w)
+ if c.Session.UserId != hook.UserId && !c.App.SessionHasPermissionToTeam(c.Session, hook.TeamId, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) {
+ c.LogAudit("fail - inappropriate permissions")
+ c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_WEBHOOKS)
+ return
}
+
+ if err = c.App.DeleteIncomingWebhook(hookId); err != nil {
+ c.Err = err
+ return
+ }
+
+ ReturnStatusOK(w)
}
}
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 cd9fdaa66..27227d271 100644
--- a/app/app.go
+++ b/app/app.go
@@ -8,6 +8,7 @@ import (
"html/template"
"net"
"net/http"
+ "reflect"
"strings"
"sync"
"sync/atomic"
@@ -26,6 +27,8 @@ import (
"github.com/mattermost/mattermost-server/utils"
)
+const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete"
+
type App struct {
goroutineCount int32
goroutineExitSignal chan struct{}
@@ -71,7 +74,6 @@ type App struct {
htmlTemplateWatcher *utils.HTMLTemplateWatcher
sessionCache *utils.Cache
- roles map[string]*model.Role
configListenerId string
licenseListenerId string
disableConfigWatch bool
@@ -155,7 +157,6 @@ func New(options ...Option) (outApp *App, outErr error) {
})
app.regenerateClientConfig()
- app.setDefaultRolesBasedOnConfig()
l4g.Info(utils.T("api.server.new_server.init.info"))
@@ -196,7 +197,6 @@ func New(options ...Option) (outApp *App, outErr error) {
func (a *App) configOrLicenseListener() {
a.regenerateClientConfig()
- a.setDefaultRolesBasedOnConfig()
}
func (a *App) Shutdown() {
@@ -495,3 +495,57 @@ func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey())
}
+
+// 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(), a.License() != nil)
+
+ 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 09f8725d7..c2841ec53 100644
--- a/app/app_test.go
+++ b/app/app_test.go
@@ -5,11 +5,11 @@ package app
import (
"flag"
+ "fmt"
"os"
"testing"
l4g "github.com/alecthomas/log4go"
-
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -72,3 +72,346 @@ 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_ADD_REACTION.Id,
+ model.PERMISSION_REMOVE_REACTION.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_USE_SLASH_COMMANDS.Id,
+ model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
+ model.PERMISSION_DELETE_POST.Id,
+ model.PERMISSION_EDIT_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_REMOVE_OTHERS_REACTIONS.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_ADD_REACTION.Id,
+ model.PERMISSION_REMOVE_REACTION.Id,
+ model.PERMISSION_UPLOAD_FILE.Id,
+ model.PERMISSION_GET_PUBLIC_LINK.Id,
+ model.PERMISSION_CREATE_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,
+ model.PERMISSION_EDIT_POST.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.
+ 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 })
+ }()
+
+ 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.SetLicense(model.NewTestLicense())
+
+ // 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_ADD_REACTION.Id,
+ model.PERMISSION_REMOVE_REACTION.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_USE_SLASH_COMMANDS.Id,
+ model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
+ model.PERMISSION_DELETE_POST.Id,
+ model.PERMISSION_EDIT_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_REMOVE_OTHERS_REACTIONS.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_ADD_REACTION.Id,
+ model.PERMISSION_REMOVE_REACTION.Id,
+ model.PERMISSION_UPLOAD_FILE.Id,
+ model.PERMISSION_GET_PUBLIC_LINK.Id,
+ model.PERMISSION_CREATE_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,
+ model.PERMISSION_EDIT_POST.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, fmt.Sprintf("'%v' did not have expected permissions", name))
+ }
+
+ // Remove the license.
+ th.App.SetLicense(nil)
+
+ // 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 01f5b0102..6c2273c6e 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() {
@@ -102,6 +107,9 @@ func setupTestHelper(enterprise bool) *TestHelper {
}
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 })
@@ -217,6 +225,7 @@ func (me *TestHelper) CreatePost(channel *model.Channel) *model.Post {
UserId: me.BasicUser.Id,
ChannelId: channel.Id,
Message: "message_" + id,
+ CreateAt: model.GetMillis() - 10000,
}
utils.DisableDebugLogForTest()
@@ -326,3 +335,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 4231cac77..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
}
}
@@ -181,17 +181,33 @@ func (a *App) HasPermissionToChannelByPost(askingUserId string, postId string, p
return a.HasPermissionTo(askingUserId, permission)
}
-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) HasPermissionToUser(askingUserId string, userId string) bool {
+ if askingUserId == userId {
+ return true
+ }
+
+ if a.HasPermissionTo(askingUserId, model.PERMISSION_EDIT_OTHER_USERS) {
+ return true
+ }
+
+ return false
+}
+
+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 49a797f15..eadb94c2f 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -439,6 +439,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/diagnostics.go b/app/diagnostics.go
index 4cff5f02a..b08c4f86b 100644
--- a/app/diagnostics.go
+++ b/app/diagnostics.go
@@ -249,7 +249,7 @@ func (a *App) trackConfig() {
a.SendDiagnostic(TRACK_CONFIG_TEAM, map[string]interface{}{
"enable_user_creation": cfg.TeamSettings.EnableUserCreation,
- "enable_team_creation": cfg.TeamSettings.EnableTeamCreation,
+ "enable_team_creation": *cfg.TeamSettings.EnableTeamCreation,
"restrict_team_invite": *cfg.TeamSettings.RestrictTeamInvite,
"restrict_public_channel_creation": *cfg.TeamSettings.RestrictPublicChannelCreation,
"restrict_private_channel_creation": *cfg.TeamSettings.RestrictPrivateChannelCreation,
diff --git a/app/import_test.go b/app/import_test.go
index 23213d81b..073741b19 100644
--- a/app/import_test.go
+++ b/app/import_test.go
@@ -412,10 +412,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.")
@@ -479,12 +475,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 {
@@ -517,12 +507,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/license.go b/app/license.go
index c12f23d1d..148b10317 100644
--- a/app/license.go
+++ b/app/license.go
@@ -113,7 +113,6 @@ func (a *App) License() *model.License {
func (a *App) SetLicense(license *model.License) bool {
defer func() {
- a.setDefaultRolesBasedOnConfig()
for _, listener := range a.licenseListeners {
listener()
}
diff --git a/app/post.go b/app/post.go
index d9445155b..4ce009369 100644
--- a/app/post.go
+++ b/app/post.go
@@ -124,7 +124,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
if a.License() != nil && *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)
}
@@ -326,13 +326,6 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
} else {
oldPost = result.Data.(*model.PostList).Posts[post.Id]
- if a.License() != nil {
- if *a.Config().ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_NEVER && post.Message != oldPost.Message {
- err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_denied.app_error", nil, "", http.StatusForbidden)
- return nil, err
- }
- }
-
if oldPost == nil {
err := model.NewAppError("UpdatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id, http.StatusBadRequest)
return nil, err
@@ -349,7 +342,7 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
}
if a.License() != nil {
- if *a.Config().ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_TIME_LIMIT && model.GetMillis() > oldPost.CreateAt+int64(*a.Config().ServiceSettings.PostEditTimeLimit*1000) && post.Message != oldPost.Message {
+ if *a.Config().ServiceSettings.PostEditTimeLimit != -1 && model.GetMillis() > oldPost.CreateAt+int64(*a.Config().ServiceSettings.PostEditTimeLimit*1000) && post.Message != oldPost.Message {
err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_time_limit.app_error", map[string]interface{}{"timeLimit": *a.Config().ServiceSettings.PostEditTimeLimit}, "", http.StatusBadRequest)
return nil, err
}
diff --git a/app/post_test.go b/app/post_test.go
index 8455656d7..10b957751 100644
--- a/app/post_test.go
+++ b/app/post_test.go
@@ -48,6 +48,43 @@ func TestUpdatePostEditAt(t *testing.T) {
}
}
+func TestUpdatePostTimeLimit(t *testing.T) {
+ th := Setup().InitBasic()
+ defer th.TearDown()
+
+ post := &model.Post{}
+ *post = *th.BasicPost
+
+ th.App.SetLicense(model.NewTestLicense())
+
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostEditTimeLimit = -1
+ })
+ if _, err := th.App.UpdatePost(post, true); err != nil {
+ t.Fatal(err)
+ }
+
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostEditTimeLimit = 1000000000
+ })
+ post.Message = model.NewId()
+ if _, err := th.App.UpdatePost(post, true); err != nil {
+ t.Fatal("should allow you to edit the post")
+ }
+
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostEditTimeLimit = 1
+ })
+ post.Message = model.NewId()
+ if _, err := th.App.UpdatePost(post, true); err == nil {
+ t.Fatal("should fail on update old post")
+ }
+
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostEditTimeLimit = -1
+ })
+}
+
func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
// post completes successfully. This is a regression test for PLT-6523.
diff --git a/app/role.go b/app/role.go
index 9f271ea7a..c99d8365b 100644
--- a/app/role.go
+++ b/app/role.go
@@ -1,17 +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
}
-func (a *App) setDefaultRolesBasedOnConfig() {
- a.roles = utils.DefaultRolesBasedOnConfig(a.Config(), a.License() != nil)
+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 239ce4369..a7b32af33 100644
--- a/app/team.go
+++ b/app/team.go
@@ -160,6 +160,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 e1ba0e79f..8d3ec11be 100644
--- a/app/user.go
+++ b/app/user.go
@@ -1241,6 +1241,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/commands/server.go b/cmd/commands/server.go
index 8358fe98f..9048696ca 100644
--- a/cmd/commands/server.go
+++ b/cmd/commands/server.go
@@ -86,6 +86,8 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
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/config/default.json b/config/default.json
index 908604d7e..ed0af5cc6 100644
--- a/config/default.json
+++ b/config/default.json
@@ -47,7 +47,7 @@
"RestrictCustomEmojiCreation": "all",
"RestrictPostDelete": "all",
"AllowEditPost": "always",
- "PostEditTimeLimit": 300,
+ "PostEditTimeLimit": -1,
"ExperimentalEnableAuthenticationTransfer": true,
"TimeBetweenUserTypingUpdatesMilliseconds": 5000,
"EnablePostSearch": true,
diff --git a/i18n/en.json b/i18n/en.json
index 8f0e61588..cf3e88b5a 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -48,6 +48,10 @@
"translation": "September"
},
{
+ "id": "api.roles.patch_roles.license.error",
+ "translation": "Your current license does not support advanced permissions."
+ },
+ {
"id": "api.admin.add_certificate.no_file.app_error",
"translation": "No file under 'certificate' in request."
},
@@ -2427,10 +2431,18 @@
"translation": "Your email address for {{.TeamDisplayName}} has been changed to {{.NewEmail}}.<br>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"
},
@@ -6599,6 +6611,26 @@
"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/client4.go b/model/client4.go
index 231d8402d..3346cc6eb 100644
--- a/model/client4.go
+++ b/model/client4.go
@@ -310,6 +310,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")
}
@@ -3263,6 +3267,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 f060c4ac8..cf9e3f9f2 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/config.go b/model/config.go
index 4e13ca16e..5cc2488e0 100644
--- a/model/config.go
+++ b/model/config.go
@@ -424,7 +424,7 @@ func (s *ServiceSettings) SetDefaults() {
}
if s.PostEditTimeLimit == nil {
- s.PostEditTimeLimit = NewInt(300)
+ s.PostEditTimeLimit = NewInt(-1)
}
if s.EnablePreviewFeatures == nil {
@@ -967,7 +967,7 @@ func (s *ThemeSettings) SetDefaults() {
type TeamSettings struct {
SiteName string
MaxUsersPerTeam *int
- EnableTeamCreation bool
+ EnableTeamCreation *bool
EnableUserCreation bool
EnableOpenServer *bool
RestrictCreationToDomains string
@@ -1090,6 +1090,10 @@ func (s *TeamSettings) SetDefaults() {
if s.ExperimentalPrimaryTeam == nil {
s.ExperimentalPrimaryTeam = NewString("")
}
+
+ if s.EnableTeamCreation == nil {
+ s.EnableTeamCreation = NewBool(true)
+ }
}
type ClientRequirements struct {
diff --git a/model/authorization.go b/model/permission.go
index 34faf8bba..703f6c71e 100644
--- a/model/authorization.go
+++ b/model/permission.go
@@ -3,17 +3,17 @@
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"`
-}
-
-type Role struct {
- Id string `json:"id"`
- Name string `json:"name"`
- Description string `json:"description"`
- Permissions []string `json:"permissions"`
+ Scope string `json:"scope"`
}
var PERMISSION_INVITE_USER *Permission
@@ -40,6 +40,9 @@ 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_ADD_REACTION *Permission
+var PERMISSION_REMOVE_REACTION *Permission
+var PERMISSION_REMOVE_OTHERS_REACTIONS *Permission
var PERMISSION_PERMANENT_DELETE_USER *Permission
var PERMISSION_UPLOAD_FILE *Permission
var PERMISSION_GET_PUBLIC_LINK *Permission
@@ -64,446 +67,376 @@ 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
+// 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
-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"
-)
+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__publicchannel_properties",
+ "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_ADD_REACTION = &Permission{
+ "add_reaction",
+ "authentication.permissions.add_reaction.name",
+ "authentication.permissions.add_reaction.description",
+ PERMISSION_SCOPE_CHANNEL,
+ }
+ PERMISSION_REMOVE_REACTION = &Permission{
+ "remove_reaction",
+ "authentication.permissions.remove_reaction.name",
+ "authentication.permissions.remove_reaction.description",
+ PERMISSION_SCOPE_CHANNEL,
+ }
+ PERMISSION_REMOVE_OTHERS_REACTIONS = &Permission{
+ "remove_others_reactions",
+ "authentication.permissions.remove_others_reactions.name",
+ "authentication.permissions.remove_others_reactions.description",
+ PERMISSION_SCOPE_CHANNEL,
}
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",
+ "manage_system_wide_oauth",
+ "authentication.permissions.manage_system_wide_oauth.name",
+ "authentication.permissions.manage_system_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",
- }
-}
-
-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,
- },
+ PERMISSION_SCOPE_SYSTEM,
}
- 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...,
- ),
+ 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_ADD_REACTION,
+ PERMISSION_REMOVE_REACTION,
+ PERMISSION_REMOVE_OTHERS_REACTIONS,
+ 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()
- initializeDefaultRoles()
}
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..5c2cf8f5b
--- /dev/null
+++ b/model/role.go
@@ -0,0 +1,350 @@
+// 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"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ 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
+ }
+}
+
+// Returns an array of permissions that are in either role.Permissions
+// or patch.Permissions, but not both.
+func PermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
+ var result []string
+
+ if patch.Permissions == nil {
+ return result
+ }
+
+ roleMap := make(map[string]bool)
+ patchMap := make(map[string]bool)
+
+ for _, permission := range role.Permissions {
+ roleMap[permission] = true
+ }
+
+ for _, permission := range *patch.Permissions {
+ patchMap[permission] = true
+ }
+
+ for _, permission := range role.Permissions {
+ if !patchMap[permission] {
+ result = append(result, permission)
+ }
+ }
+
+ for _, permission := range *patch.Permissions {
+ if !roleMap[permission] {
+ result = append(result, permission)
+ }
+ }
+
+ return result
+}
+
+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_ADD_REACTION.Id,
+ PERMISSION_REMOVE_REACTION.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
+ PERMISSION_UPLOAD_FILE.Id,
+ PERMISSION_GET_PUBLIC_LINK.Id,
+ PERMISSION_CREATE_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,
+ PERMISSION_REMOVE_OTHERS_REACTIONS.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 0f215b0e7..c3a6ec04a 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 2bf8b2a65..72ad6a92b 100644
--- a/model/user_test.go
+++ b/model/user_test.go
@@ -287,11 +287,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 76326ee3f..9013ab428 100644
--- a/model/websocket_message.go
+++ b/model/websocket_message.go
@@ -44,6 +44,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"
WEBSOCKET_EVENT_LICENSE_CHANGED = "license_changed"
WEBSOCKET_EVENT_CONFIG_CHANGED = "config_changed"
)
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 d5e654019..80fe3cb24 100644
--- a/store/layered_store_supplier.go
+++ b/store/layered_store_supplier.go
@@ -29,4 +29,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..8cbde0a23
--- /dev/null
+++ b/store/local_cache_supplier_roles.go
@@ -0,0 +1,70 @@
+// 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 {
+ // Roles are cached by name, as that is most commonly how they are looked up.
+ // This means that no caching is supported on roles being looked up by ID.
+ 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..751227be9 100644
--- a/store/redis_supplier.go
+++ b/store/redis_supplier.go
@@ -5,14 +5,12 @@ package store
import (
"bytes"
- "context"
"encoding/gob"
"time"
l4g "github.com/alecthomas/log4go"
"github.com/go-redis/redis"
- "github.com/mattermost/mattermost-server/model"
)
const REDIS_EXPIRY_TIME = 30 * time.Minute
@@ -87,48 +85,3 @@ func (s *RedisSupplier) SetChainNext(next LayeredStoreSupplier) {
func (s *RedisSupplier) Next() LayeredStoreSupplier {
return s.next
}
-
-func (s *RedisSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
- if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
- l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
- }
- return s.Next().ReactionSave(ctx, reaction, hints...)
-}
-
-func (s *RedisSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
- if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
- l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
- }
- return s.Next().ReactionDelete(ctx, reaction, hints...)
-}
-
-func (s *RedisSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
- var resultdata []*model.Reaction
- found, err := s.load("reactions:"+postId, &resultdata)
- if found {
- result := NewSupplierResult()
- result.Data = resultdata
- return result
- }
- if err != nil {
- l4g.Error("Redis encountered an error on read: " + err.Error())
- }
-
- result := s.Next().ReactionGetForPost(ctx, postId, hints...)
-
- if err := s.save("reactions:"+postId, result.Data, REDIS_EXPIRY_TIME); err != nil {
- l4g.Error("Redis encountered and error on write: " + err.Error())
- }
-
- return result
-}
-
-func (s *RedisSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
- // Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
- return s.Next().ReactionDeleteAllWithEmojiName(ctx, emojiName, hints...)
-}
-
-func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
- // Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
- return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...)
-}
diff --git a/store/redis_supplier_reactions.go b/store/redis_supplier_reactions.go
new file mode 100644
index 000000000..cece8113d
--- /dev/null
+++ b/store/redis_supplier_reactions.go
@@ -0,0 +1,57 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package store
+
+import (
+ "context"
+
+ l4g "github.com/alecthomas/log4go"
+
+ "github.com/mattermost/mattermost-server/model"
+)
+
+func (s *RedisSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
+ l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
+ }
+ return s.Next().ReactionSave(ctx, reaction, hints...)
+}
+
+func (s *RedisSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
+ l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
+ }
+ return s.Next().ReactionDelete(ctx, reaction, hints...)
+}
+
+func (s *RedisSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ var resultdata []*model.Reaction
+ found, err := s.load("reactions:"+postId, &resultdata)
+ if found {
+ result := NewSupplierResult()
+ result.Data = resultdata
+ return result
+ }
+ if err != nil {
+ l4g.Error("Redis encountered an error on read: " + err.Error())
+ }
+
+ result := s.Next().ReactionGetForPost(ctx, postId, hints...)
+
+ if err := s.save("reactions:"+postId, result.Data, REDIS_EXPIRY_TIME); err != nil {
+ l4g.Error("Redis encountered and error on write: " + err.Error())
+ }
+
+ return result
+}
+
+func (s *RedisSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ // Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
+ return s.Next().ReactionDeleteAllWithEmojiName(ctx, emojiName, hints...)
+}
+
+func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ // Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
+ return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...)
+}
diff --git a/store/redis_supplier_roles.go b/store/redis_supplier_roles.go
new file mode 100644
index 000000000..170420f1f
--- /dev/null
+++ b/store/redis_supplier_roles.go
@@ -0,0 +1,89 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package store
+
+import (
+ "context"
+ "fmt"
+
+ l4g "github.com/alecthomas/log4go"
+
+ "github.com/mattermost/mattermost-server/model"
+)
+
+func (s *RedisSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ key := buildRedisKeyForRoleName(role.Name)
+
+ if err := s.client.Del(key).Err(); err != nil {
+ l4g.Error("Redis failed to remove key " + key + " Error: " + err.Error())
+ }
+
+ return s.Next().RoleSave(ctx, role, hints...)
+}
+
+func (s *RedisSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ // Roles are cached by name, as that is most commonly how they are looked up.
+ // This means that no caching is supported on roles being looked up by ID.
+ return s.Next().RoleGet(ctx, roleId, hints...)
+}
+
+func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ key := buildRedisKeyForRoleName(name)
+
+ var role *model.Role
+ found, err := s.load(key, &role)
+ if err != nil {
+ l4g.Error("Redis encountered an error on read: " + err.Error())
+ } else if found {
+ result := NewSupplierResult()
+ result.Data = role
+ return result
+ }
+
+ result := s.Next().RoleGetByName(ctx, name, hints...)
+
+ if result.Err == nil {
+ if err := s.save(key, result.Data, REDIS_EXPIRY_TIME); err != nil {
+ l4g.Error("Redis encountered and error on write: " + err.Error())
+ }
+ }
+
+ return result
+}
+
+func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
+ var foundRoles []*model.Role
+ var rolesToQuery []string
+
+ for _, roleName := range roleNames {
+ var role *model.Role
+ found, err := s.load(buildRedisKeyForRoleName(roleName), &role)
+ if err == nil && found {
+ foundRoles = append(foundRoles, role)
+ } else {
+ rolesToQuery = append(rolesToQuery, roleName)
+ if err != nil {
+ l4g.Error("Redis encountered an error on read: " + err.Error())
+ }
+ }
+ }
+
+ result := s.Next().RoleGetByNames(ctx, rolesToQuery, hints...)
+
+ if result.Err == nil {
+ rolesFound := result.Data.([]*model.Role)
+ for _, role := range rolesFound {
+ if err := s.save(buildRedisKeyForRoleName(role.Name), role, REDIS_EXPIRY_TIME); err != nil {
+ l4g.Error("Redis encountered and error on write: " + err.Error())
+ }
+ }
+ result.Data = append(foundRoles, result.Data.([]*model.Role)...)
+ }
+
+ return result
+}
+
+func buildRedisKeyForRoleName(roleName string) string {
+ return fmt.Sprintf("roles:%s", roleName)
+}
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..f9ce53788
--- /dev/null
+++ b/store/sqlstore/role_supplier.go
@@ -0,0 +1,175 @@
+// 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
+ CreateAt int64
+ UpdateAt int64
+ DeleteAt int64
+ 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,
+ CreateAt: role.CreateAt,
+ UpdateAt: role.UpdateAt,
+ DeleteAt: role.DeleteAt,
+ 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,
+ CreateAt: role.CreateAt,
+ UpdateAt: role.UpdateAt,
+ DeleteAt: role.DeleteAt,
+ 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()
+ dbRole.CreateAt = model.GetMillis()
+ dbRole.UpdateAt = dbRole.CreateAt
+ 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 {
+ dbRole.UpdateAt = model.GetMillis()
+ 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/sqlstore/upgrade.go b/store/sqlstore/upgrade.go
index d528e464d..174b434f8 100644
--- a/store/sqlstore/upgrade.go
+++ b/store/sqlstore/upgrade.go
@@ -374,6 +374,10 @@ func UpgradeDatabaseToVersion48(sqlStore SqlStore) {
}
func UpgradeDatabaseToVersion49(sqlStore SqlStore) {
+ // This version of Mattermost includes an App-Layer migration which migrates from hard-coded roles configured by
+ // a number of parameters in `config.json` to a `Roles` table in the database. The migration code can be seen
+ // in the file `app/app.go` in the function `DoAdvancedPermissionsMigration()`.
+
//TODO: Uncomment the following condition when version 4.9.0 is released
//if shouldPerformUpgrade(sqlStore, VERSION_4_8_0, VERSION_4_9_0) {
sqlStore.CreateColumnIfNotExists("Teams", "LastTeamIconUpdate", "bigint", "bigint", "0")
diff --git a/store/store.go b/store/store.go
index 773dfff02..79a14baba 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
@@ -469,3 +470,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 42815b807..16f33bc1a 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, isLicensed bool) 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, isLicensed bool) map[string]*model.Role {
if isLicensed {
switch *cfg.TeamSettings.RestrictPublicChannelCreation {
case model.PERMISSIONS_ALL:
@@ -222,8 +215,8 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m
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, isLicensed bool) map[string]*m
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,
@@ -272,12 +260,35 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m
)
}
- if cfg.TeamSettings.EnableTeamCreation {
+ if *cfg.TeamSettings.EnableTeamCreation {
roles[model.SYSTEM_USER_ROLE_ID].Permissions = append(
roles[model.SYSTEM_USER_ROLE_ID].Permissions,
model.PERMISSION_CREATE_TEAM.Id,
)
}
+ if isLicensed {
+ switch *cfg.ServiceSettings.AllowEditPost {
+ case model.ALLOW_EDIT_POST_ALWAYS, model.ALLOW_EDIT_POST_TIME_LIMIT:
+ roles[model.CHANNEL_USER_ROLE_ID].Permissions = append(
+ roles[model.CHANNEL_USER_ROLE_ID].Permissions,
+ model.PERMISSION_EDIT_POST.Id,
+ )
+ roles[model.SYSTEM_ADMIN_ROLE_ID].Permissions = append(
+ roles[model.SYSTEM_ADMIN_ROLE_ID].Permissions,
+ model.PERMISSION_EDIT_POST.Id,
+ )
+ }
+ } else {
+ roles[model.CHANNEL_USER_ROLE_ID].Permissions = append(
+ roles[model.CHANNEL_USER_ROLE_ID].Permissions,
+ model.PERMISSION_EDIT_POST.Id,
+ )
+ roles[model.SYSTEM_ADMIN_ROLE_ID].Permissions = append(
+ roles[model.SYSTEM_ADMIN_ROLE_ID].Permissions,
+ model.PERMISSION_EDIT_POST.Id,
+ )
+ }
+
return roles
}
diff --git a/utils/authorization_test.go b/utils/authorization_test.go
new file mode 100644
index 000000000..8c78dcbda
--- /dev/null
+++ b/utils/authorization_test.go
@@ -0,0 +1,133 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package utils
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "reflect"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/mattermost/mattermost-server/model"
+)
+
+type RoleState struct {
+ RoleName string `json:"roleName"`
+ Permission string `json:"permission"`
+ ShouldHave bool `json:"shouldHave"`
+}
+
+func mockConfig() *model.Config {
+ config := model.Config{}
+ config.SetDefaults()
+ return &config
+}
+
+func mapping() (map[string]map[string][]RoleState, error) {
+
+ policiesRolesMapping := make(map[string]map[string][]RoleState)
+
+ raw, err := ioutil.ReadFile("./policies-roles-mapping.json")
+ if err != nil {
+ return policiesRolesMapping, err
+ }
+
+ var f map[string]interface{}
+ err = json.Unmarshal(raw, &f)
+ if err != nil {
+ return policiesRolesMapping, err
+ }
+
+ for policyName, value := range f {
+
+ capitalizedName := fmt.Sprintf("%v%v", strings.ToUpper(policyName[:1]), policyName[1:])
+ policiesRolesMapping[capitalizedName] = make(map[string][]RoleState)
+
+ for policyValue, roleStatesMappings := range value.(map[string]interface{}) {
+
+ var roleStates []RoleState
+ for _, roleStateMapping := range roleStatesMappings.([]interface{}) {
+
+ roleStateMappingJSON, _ := json.Marshal(roleStateMapping)
+ var roleState RoleState
+ _ = json.Unmarshal(roleStateMappingJSON, &roleState)
+
+ roleStates = append(roleStates, roleState)
+
+ }
+
+ policiesRolesMapping[capitalizedName][policyValue] = roleStates
+
+ }
+
+ }
+
+ return policiesRolesMapping, nil
+}
+
+func TestSetRolePermissionsFromConfig(t *testing.T) {
+
+ mapping, err := mapping()
+ if err != nil {
+ require.NoError(t, err)
+ }
+
+ for policyName, v := range mapping {
+ for policyValue, rolesMappings := range v {
+
+ config := mockConfig()
+ updateConfig(config, policyName, policyValue)
+ roles := model.MakeDefaultRoles()
+ SetRolePermissionsFromConfig(roles, config, true)
+
+ for _, roleMappingItem := range rolesMappings {
+ role := roles[roleMappingItem.RoleName]
+
+ permission := roleMappingItem.Permission
+ hasPermission := roleHasPermission(role, permission)
+
+ if (roleMappingItem.ShouldHave && !hasPermission) || (!roleMappingItem.ShouldHave && hasPermission) {
+ wording := "not to"
+ if roleMappingItem.ShouldHave {
+ wording = "to"
+ }
+ t.Errorf("Expected '%v' %v have '%v' permission when '%v' is set to '%v'.", role.Name, wording, permission, policyName, policyValue)
+ }
+
+ }
+
+ }
+ }
+}
+
+func updateConfig(config *model.Config, key string, value string) {
+ v := reflect.ValueOf(config.ServiceSettings)
+ field := v.FieldByName(key)
+ if !field.IsValid() {
+ v = reflect.ValueOf(config.TeamSettings)
+ field = v.FieldByName(key)
+ }
+
+ switch value {
+ case "true", "false":
+ b, _ := strconv.ParseBool(value)
+ field.Elem().SetBool(b)
+ default:
+ field.Elem().SetString(value)
+ }
+}
+
+func roleHasPermission(role *model.Role, permission string) bool {
+ for _, p := range role.Permissions {
+ if p == permission {
+ return true
+ }
+ }
+ return false
+}
diff --git a/utils/config.go b/utils/config.go
index 163838a13..35a21dea4 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -337,8 +337,8 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
props["SiteURL"] = strings.TrimRight(*c.ServiceSettings.SiteURL, "/")
props["WebsocketURL"] = strings.TrimRight(*c.ServiceSettings.WebsocketURL, "/")
props["SiteName"] = c.TeamSettings.SiteName
+ props["EnableTeamCreation"] = strconv.FormatBool(*c.TeamSettings.EnableTeamCreation)
props["EnableAPIv3"] = strconv.FormatBool(*c.ServiceSettings.EnableAPIv3)
- props["EnableTeamCreation"] = strconv.FormatBool(c.TeamSettings.EnableTeamCreation)
props["EnableUserCreation"] = strconv.FormatBool(c.TeamSettings.EnableUserCreation)
props["EnableOpenServer"] = strconv.FormatBool(*c.TeamSettings.EnableOpenServer)
props["RestrictDirectMessage"] = *c.TeamSettings.RestrictDirectMessage
diff --git a/utils/policies-roles-mapping.json b/utils/policies-roles-mapping.json
new file mode 100644
index 000000000..6b09c6c72
--- /dev/null
+++ b/utils/policies-roles-mapping.json
@@ -0,0 +1,532 @@
+{
+ "restrictTeamInvite": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "invite_user",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "invite_user",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "invite_user",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "invite_user",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "invite_user",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPublicChannelCreation": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "create_public_channel",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "create_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "create_public_channel",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "create_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "create_public_channel",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPrivateChannelCreation": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "create_private_channel",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "create_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "create_private_channel",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "create_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "create_private_channel",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPublicChannelManagement": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "channel_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_public_channel_properties",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPublicChannelDeletion": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_public_channel",
+ "shouldHave": true
+ }
+ ],
+ "channel_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_public_channel",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPrivateChannelManagement": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "channel_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_properties",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPrivateChannelManageMembers": {
+ "all": [
+ {
+ "roleName": "channel_user",
+ "permission": "manage_private_channel_members",
+ "shouldHave": true
+ }
+ ],
+ "channel_admin": [
+ {
+ "roleName": "channel_user",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "channel_user",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "channel_user",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "manage_private_channel_members",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPrivateChannelDeletion": {
+ "all": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_private_channel",
+ "shouldHave": true
+ }
+ ],
+ "channel_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "team_user",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "channel_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_private_channel",
+ "shouldHave": false
+ }
+ ]
+ },
+ "allowEditPost": {
+ "always": [
+ {
+ "roleName": "channel_user",
+ "permission": "edit_post",
+ "shouldHave": true
+ },
+ {
+ "roleName": "system_admin",
+ "permission": "edit_post",
+ "shouldHave": true
+ }
+ ],
+ "never": [
+ {
+ "roleName": "channel_user",
+ "permission": "edit_post",
+ "shouldHave": false
+ },
+ {
+ "roleName": "system_admin",
+ "permission": "edit_post",
+ "shouldHave": false
+ }
+ ]
+ },
+ "restrictPostDelete": {
+ "all": [
+ {
+ "roleName": "channel_user",
+ "permission": "delete_post",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_post",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_others_posts",
+ "shouldHave": true
+ }
+ ],
+ "team_admin": [
+ {
+ "roleName": "channel_user",
+ "permission": "delete_post",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_post",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_others_posts",
+ "shouldHave": true
+ }
+ ],
+ "system_admin": [
+ {
+ "roleName": "channel_user",
+ "permission": "delete_post",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_post",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_admin",
+ "permission": "delete_others_posts",
+ "shouldHave": false
+ }
+ ]
+ },
+ "enableTeamCreation": {
+ "true": [
+ {
+ "roleName": "system_user",
+ "permission": "create_team",
+ "shouldHave": true
+ }
+ ],
+ "false": [
+ {
+ "roleName": "system_user",
+ "permission": "create_team",
+ "shouldHave": false
+ }
+ ]
+ },
+ "enableOnlyAdminIntegrations": {
+ "true": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_webhooks",
+ "shouldHave": false
+ },
+ {
+ "roleName": "team_user",
+ "permission": "manage_slash_commands",
+ "shouldHave": false
+ },
+ {
+ "roleName": "system_user",
+ "permission": "manage_oauth",
+ "shouldHave": false
+ }
+ ],
+ "false": [
+ {
+ "roleName": "team_user",
+ "permission": "manage_webhooks",
+ "shouldHave": true
+ },
+ {
+ "roleName": "team_user",
+ "permission": "manage_slash_commands",
+ "shouldHave": true
+ },
+ {
+ "roleName": "system_user",
+ "permission": "manage_oauth",
+ "shouldHave": true
+ }
+ ]
+ }
+}
diff --git a/web/web_test.go b/web/web_test.go
index 7da16ab24..60122f049 100644
--- a/web/web_test.go
+++ b/web/web_test.go
@@ -55,6 +55,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) {