diff options
Diffstat (limited to 'api4')
-rw-r--r-- | api4/api.go | 5 | ||||
-rw-r--r-- | api4/apitestlib.go | 112 | ||||
-rw-r--r-- | api4/channel.go | 1 | ||||
-rw-r--r-- | api4/channel_test.go | 347 | ||||
-rw-r--r-- | api4/context.go | 23 | ||||
-rw-r--r-- | api4/oauth_test.go | 85 | ||||
-rw-r--r-- | api4/params.go | 10 | ||||
-rw-r--r-- | api4/post_test.go | 3 | ||||
-rw-r--r-- | api4/reaction.go | 12 | ||||
-rw-r--r-- | api4/reaction_test.go | 464 | ||||
-rw-r--r-- | api4/role.go | 125 | ||||
-rw-r--r-- | api4/role_test.go | 206 | ||||
-rw-r--r-- | api4/team_test.go | 88 | ||||
-rw-r--r-- | api4/webhook_test.go | 72 |
14 files changed, 985 insertions, 568 deletions
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 e55ca8c8b..a788d4311 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -121,6 +121,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 }) @@ -810,3 +811,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 29dff883f..1670770c6 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 e65918707..1b74ea880 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) - sqlstore.ClearChannelCaches() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // successful delete by team admin - th.UpdateUserToTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - 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) - sqlstore.ClearChannelCaches() - - _, resp = Client.DeleteChannel(publicChannel6.Id) - CheckForbiddenStatus(t, resp) - - _, resp = Client.DeleteChannel(privateChannel7.Id) - CheckForbiddenStatus(t, resp) - - // cannot delete by team admin - th.UpdateUserToTeamAdmin(th.BasicUser, team) - th.App.InvalidateAllCaches() - 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 - }) + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() - Client.Login(user2.Username, user2.Password) - privateChannel = th.CreatePrivateChannel() - _, resp = Client.AddChannelMember(privateChannel.Id, user.Id) - CheckNoError(t, resp) - Client.Logout() - - Client.Login(user.Username, user.Password) - _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) - CheckNoError(t, resp) - Client.Logout() - - // Add a license - 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 - }) + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() - privateChannel := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) - CheckNoError(t, resp) - _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) - CheckNoError(t, resp) - - // Add a license - 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,46 +1763,7 @@ 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..e7654011d --- /dev/null +++ b/api4/role.go @@ -0,0 +1,125 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "net/http" + + "github.com/mattermost/mattermost-server/model" +) + +func (api *API) InitRole() { + api.BaseRoutes.Roles.Handle("/{role_id:[A-Za-z0-9]+}", api.ApiSessionRequiredTrustRequester(getRole)).Methods("GET") + api.BaseRoutes.Roles.Handle("/name/{role_name:[a-z0-9_]+}", api.ApiSessionRequiredTrustRequester(getRoleByName)).Methods("GET") + api.BaseRoutes.Roles.Handle("/names", api.ApiSessionRequiredTrustRequester(getRolesByNames)).Methods("POST") + api.BaseRoutes.Roles.Handle("/{role_id:[A-Za-z0-9]+}/patch", api.ApiSessionRequired(patchRole)).Methods("PUT") +} + +func getRole(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleId() + if c.Err != nil { + return + } + + if role, err := c.App.GetRole(c.Params.RoleId); err != nil { + c.Err = err + return + } else { + w.Write([]byte(role.ToJson())) + } +} + +func getRoleByName(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleName() + if c.Err != nil { + return + } + + if role, err := c.App.GetRoleByName(c.Params.RoleName); err != nil { + c.Err = err + return + } else { + w.Write([]byte(role.ToJson())) + } +} + +func getRolesByNames(c *Context, w http.ResponseWriter, r *http.Request) { + rolenames := model.ArrayFromJson(r.Body) + + if len(rolenames) == 0 { + c.SetInvalidParam("rolenames") + return + } + + for _, rolename := range rolenames { + if !model.IsValidRoleName(rolename) { + c.SetInvalidParam("rolename") + return + } + } + + if roles, err := c.App.GetRolesByNames(rolenames); err != nil { + c.Err = err + return + } else { + w.Write([]byte(model.RoleListToJson(roles))) + } +} + +func patchRole(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireRoleId() + if c.Err != nil { + return + } + + patch := model.RolePatchFromJson(r.Body) + if patch == nil { + c.SetInvalidParam("role") + return + } + + oldRole, err := c.App.GetRole(c.Params.RoleId) + if err != nil { + c.Err = err + return + } + + if c.App.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..3fbf6808d --- /dev/null +++ b/api4/role_test.go @@ -0,0 +1,206 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" +) + +func TestGetRole(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role) + assert.Nil(t, res1.Err) + role = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role.Id) + + received, resp := th.Client.GetRole(role.Id) + CheckNoError(t, resp) + + assert.Equal(t, received.Id, role.Id) + assert.Equal(t, received.Name, role.Name) + assert.Equal(t, received.DisplayName, role.DisplayName) + assert.Equal(t, received.Description, role.Description) + assert.EqualValues(t, received.Permissions, role.Permissions) + assert.Equal(t, received.SchemeManaged, role.SchemeManaged) + + _, resp = th.SystemAdminClient.GetRole("1234") + CheckBadRequestStatus(t, resp) + + _, resp = th.SystemAdminClient.GetRole(model.NewId()) + CheckNotFoundStatus(t, resp) +} + +func TestGetRoleByName(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role) + assert.Nil(t, res1.Err) + role = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role.Id) + + received, resp := th.Client.GetRoleByName(role.Name) + CheckNoError(t, resp) + + assert.Equal(t, received.Id, role.Id) + assert.Equal(t, received.Name, role.Name) + assert.Equal(t, received.DisplayName, role.DisplayName) + assert.Equal(t, received.Description, role.Description) + assert.EqualValues(t, received.Permissions, role.Permissions) + assert.Equal(t, received.SchemeManaged, role.SchemeManaged) + + _, resp = th.SystemAdminClient.GetRoleByName(strings.Repeat("abcdefghij", 10)) + CheckBadRequestStatus(t, resp) + + _, resp = th.SystemAdminClient.GetRoleByName(model.NewId()) + CheckNotFoundStatus(t, resp) +} + +func TestGetRolesByNames(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel"}, + SchemeManaged: true, + } + role2 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "delete_private_channel"}, + SchemeManaged: true, + } + role3 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "manage_public_channel_properties"}, + SchemeManaged: true, + } + + res1 := <-th.App.Srv.Store.Role().Save(role1) + assert.Nil(t, res1.Err) + role1 = res1.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role1.Id) + + res2 := <-th.App.Srv.Store.Role().Save(role2) + assert.Nil(t, res2.Err) + role2 = res2.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role2.Id) + + res3 := <-th.App.Srv.Store.Role().Save(role3) + assert.Nil(t, res3.Err) + role3 = res3.Data.(*model.Role) + defer th.App.Srv.Store.Job().Delete(role3.Id) + + // Check all three roles can be found. + received, resp := th.Client.GetRolesByNames([]string{role1.Name, role2.Name, role3.Name}) + CheckNoError(t, resp) + + assert.Contains(t, received, role1) + assert.Contains(t, received, role2) + assert.Contains(t, received, role3) + + // Check a list of invalid roles. + // TODO: Confirm whether no error for invalid role names is intended. + received, resp = th.Client.GetRolesByNames([]string{model.NewId(), model.NewId()}) + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetRolesByNames([]string{}) + CheckBadRequestStatus(t, resp) +} + +func TestPatchRole(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + role := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{"manage_system", "create_public_channel", "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 04a0e9ae4..faac81312 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) @@ -1288,16 +1294,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. @@ -1307,38 +1315,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) @@ -1476,16 +1472,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. @@ -1495,30 +1493,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_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 |