diff --git a/api4/post_test.go b/api4/post_test.go index f1e19dd567..a847982e09 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -112,6 +112,30 @@ func TestCreatePost(t *testing.T) { assert.Equal(t, model.StringArray{fileId}, actualPostWithFiles.FileIds) }) + t.Run("creates a post that has channel mentions without the USE_CHANNEL_MENTIONS Permission", func(t *testing.T) { + defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions()) + + th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) + + post.RootId = rpost.Id + post.ParentId = rpost.Id + post.Message = "a post with @channel" + _, resp = Client.CreatePost(post) + CheckNoError(t, resp) + + post.RootId = rpost.Id + post.ParentId = rpost.Id + post.Message = "a post with @all" + _, resp = Client.CreatePost(post) + CheckNoError(t, resp) + + post.RootId = rpost.Id + post.ParentId = rpost.Id + post.Message = "a post with @here" + _, resp = Client.CreatePost(post) + CheckNoError(t, resp) + }) + post.RootId = "" post.ParentId = "" post.Type = model.POST_SYSTEM_GENERIC diff --git a/app/app_test.go b/app/app_test.go index 877c426913..b0f0f65d24 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -77,6 +77,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_UPLOAD_FILE.Id, model.PERMISSION_GET_PUBLIC_LINK.Id, model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.PERMISSION_USE_SLASH_COMMANDS.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, @@ -101,9 +102,11 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { }, "team_post_all": { model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "team_post_all_public": { model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "team_admin": { model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, @@ -130,9 +133,11 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { }, "system_post_all": { model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "system_post_all_public": { model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "system_user_access_token": { model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, @@ -191,6 +196,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_UPLOAD_FILE.Id, model.PERMISSION_GET_PUBLIC_LINK.Id, model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.PERMISSION_USE_SLASH_COMMANDS.Id, model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, model.PERMISSION_MANAGE_TEAM.Id, @@ -262,6 +268,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_UPLOAD_FILE.Id, model.PERMISSION_GET_PUBLIC_LINK.Id, model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.PERMISSION_USE_SLASH_COMMANDS.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id, model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id, @@ -284,9 +291,11 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { }, "team_post_all": { model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "team_post_all_public": { model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "team_admin": { model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, @@ -315,9 +324,11 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { }, "system_post_all": { model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "system_post_all_public": { model.PERMISSION_CREATE_POST_PUBLIC.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, }, "system_user_access_token": { model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, @@ -376,6 +387,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_UPLOAD_FILE.Id, model.PERMISSION_GET_PUBLIC_LINK.Id, model.PERMISSION_CREATE_POST.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.PERMISSION_USE_SLASH_COMMANDS.Id, model.PERMISSION_REMOVE_USER_FROM_TEAM.Id, model.PERMISSION_MANAGE_TEAM.Id, @@ -537,6 +549,7 @@ func TestDoEmojisPermissionsMigration(t *testing.T) { model.PERMISSION_DELETE_EMOJIS.Id, model.PERMISSION_DELETE_OTHERS_EMOJIS.Id, model.PERMISSION_VIEW_MEMBERS.Id, + model.PERMISSION_USE_CHANNEL_MENTIONS.Id, } sort.Strings(expectedSystemAdmin) diff --git a/app/notification.go b/app/notification.go index 0f69282700..267b4f0652 100644 --- a/app/notification.go +++ b/app/notification.go @@ -624,6 +624,10 @@ func getMentionsEnabledFields(post *model.Post) model.StringArray { // allowChannelMentions returns whether or not the channel mentions are allowed for the given post. func (a *App) allowChannelMentions(post *model.Post, numProfiles int) bool { + if !a.HasPermissionToChannel(post.UserId, post.ChannelId, model.PERMISSION_USE_CHANNEL_MENTIONS) { + return false + } + if post.Type == model.POST_HEADER_CHANGE || post.Type == model.POST_PURPOSE_CHANGE { return false } diff --git a/app/notification_test.go b/app/notification_test.go index 2e3d17bb53..a2a5d72224 100644 --- a/app/notification_test.go +++ b/app/notification_test.go @@ -876,47 +876,37 @@ func TestGetExplicitMentionsAtHere(t *testing.T) { } func TestAllowChannelMentions(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + post := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id} + t.Run("should return true for a regular post with few channel members", func(t *testing.T) { - th := Setup(t) - defer th.TearDown() - - post := &model.Post{} - allowChannelMentions := th.App.allowChannelMentions(post, 5) - assert.True(t, allowChannelMentions) }) t.Run("should return false for a channel header post", func(t *testing.T) { - th := Setup(t) - defer th.TearDown() - - post := &model.Post{Type: model.POST_HEADER_CHANGE} - - allowChannelMentions := th.App.allowChannelMentions(post, 5) - + headerChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_HEADER_CHANGE} + allowChannelMentions := th.App.allowChannelMentions(headerChangePost, 5) assert.False(t, allowChannelMentions) }) t.Run("should return false for a channel purpose post", func(t *testing.T) { - th := Setup(t) - defer th.TearDown() - - post := &model.Post{Type: model.POST_PURPOSE_CHANGE} - - allowChannelMentions := th.App.allowChannelMentions(post, 5) - + purposeChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_PURPOSE_CHANGE} + allowChannelMentions := th.App.allowChannelMentions(purposeChangePost, 5) assert.False(t, allowChannelMentions) }) t.Run("should return false for a regular post with many channel members", func(t *testing.T) { - th := Setup(t) - defer th.TearDown() - - post := &model.Post{} - allowChannelMentions := th.App.allowChannelMentions(post, int(*th.App.Config().TeamSettings.MaxNotificationsPerChannel)+1) + assert.False(t, allowChannelMentions) + }) + t.Run("should return false for a post where the post user does not have USE_CHANNEL_MENTIONS permission", func(t *testing.T) { + defer th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) + allowChannelMentions := th.App.allowChannelMentions(post, 5) assert.False(t, allowChannelMentions) }) } diff --git a/app/permissions_migrations.go b/app/permissions_migrations.go index 5e5561f692..7cd9b2c7b0 100644 --- a/app/permissions_migrations.go +++ b/app/permissions_migrations.go @@ -24,6 +24,7 @@ const ( MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER = "remove_channel_manage_delete_from_team_user" MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION = "view_members_new_permission" MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions" + MIGRATION_KEY_ADD_USE_CHANNEL_MENTIONS_PERMISSION = "add_use_channel_mentions_permission" PERMISSION_MANAGE_SYSTEM = "manage_system" PERMISSION_MANAGE_EMOJIS = "manage_emojis" @@ -56,6 +57,9 @@ const ( PERMISSION_INVITE_GUEST = "invite_guest" PERMISSION_PROMOTE_GUEST = "promote_guest" PERMISSION_DEMOTE_TO_GUEST = "demote_to_guest" + PERMISSION_USE_CHANNEL_MENTIONS = "use_channel_mentions" + PERMISSION_CREATE_POST = "create_post" + PERMISSION_CREATE_POST_PUBLIC = "create_post_public" ) func isRole(role string) func(string, map[string]map[string]bool) bool { @@ -287,6 +291,15 @@ func getAddManageGuestsPermissionsMigration() permissionsMap { } } +func getAddUseMentionChannelsPermissionMigration() permissionsMap { + return permissionsMap{ + permissionTransformation{ + On: permissionOr(permissionExists(PERMISSION_CREATE_POST), permissionExists(PERMISSION_CREATE_POST_PUBLIC)), + Add: []string{PERMISSION_USE_CHANNEL_MENTIONS}, + }, + } +} + // DoPermissionsMigrations execute all the permissions migrations need by the current version. func (a *App) DoPermissionsMigrations() *model.AppError { PermissionsMigrations := []struct { @@ -302,6 +315,7 @@ func (a *App) DoPermissionsMigrations() *model.AppError { {Key: MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER, Migration: removeChannelManageDeleteFromTeamUser}, {Key: MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION, Migration: getViewMembersPermissionMigration}, {Key: MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS, Migration: getAddManageGuestsPermissionsMigration}, + {Key: MIGRATION_KEY_ADD_USE_CHANNEL_MENTIONS_PERMISSION, Migration: getAddUseMentionChannelsPermissionMigration}, } for _, migration := range PermissionsMigrations { diff --git a/model/permission.go b/model/permission.go index 1acae3eec8..4871e9399a 100644 --- a/model/permission.go +++ b/model/permission.go @@ -90,6 +90,7 @@ var PERMISSION_VIEW_MEMBERS *Permission var PERMISSION_INVITE_GUEST *Permission var PERMISSION_PROMOTE_GUEST *Permission var PERMISSION_DEMOTE_TO_GUEST *Permission +var PERMISSION_USE_CHANNEL_MENTIONS *Permission // General permission that encompasses all system admin functions // in the future this could be broken up to allow access to some @@ -556,6 +557,13 @@ func initializePermissions() { PERMISSION_SCOPE_SYSTEM, } + PERMISSION_USE_CHANNEL_MENTIONS = &Permission{ + "use_channel_mentions", + "authentication.permissions.use_channel_mentions.name", + "authentication.permissions.use_channel_mentions.description", + PERMISSION_SCOPE_CHANNEL, + } + ALL_PERMISSIONS = []*Permission{ PERMISSION_INVITE_USER, PERMISSION_ADD_USER_TO_TEAM, @@ -631,6 +639,7 @@ func initializePermissions() { PERMISSION_INVITE_GUEST, PERMISSION_PROMOTE_GUEST, PERMISSION_DEMOTE_TO_GUEST, + PERMISSION_USE_CHANNEL_MENTIONS, } } diff --git a/model/role.go b/model/role.go index b1483f640c..6423ef52e5 100644 --- a/model/role.go +++ b/model/role.go @@ -187,6 +187,7 @@ func MakeDefaultRoles() map[string]*Role { PERMISSION_UPLOAD_FILE.Id, PERMISSION_EDIT_POST.Id, PERMISSION_CREATE_POST.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, PERMISSION_USE_SLASH_COMMANDS.Id, }, SchemeManaged: true, @@ -205,6 +206,7 @@ func MakeDefaultRoles() map[string]*Role { PERMISSION_UPLOAD_FILE.Id, PERMISSION_GET_PUBLIC_LINK.Id, PERMISSION_CREATE_POST.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, PERMISSION_USE_SLASH_COMMANDS.Id, }, SchemeManaged: true, @@ -253,6 +255,7 @@ func MakeDefaultRoles() map[string]*Role { Description: "authentication.roles.team_post_all.description", Permissions: []string{ PERMISSION_CREATE_POST.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, }, SchemeManaged: false, BuiltIn: true, @@ -264,6 +267,7 @@ func MakeDefaultRoles() map[string]*Role { Description: "authentication.roles.team_post_all_public.description", Permissions: []string{ PERMISSION_CREATE_POST_PUBLIC.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, }, SchemeManaged: false, BuiltIn: true, @@ -323,6 +327,7 @@ func MakeDefaultRoles() map[string]*Role { Description: "authentication.roles.system_post_all.description", Permissions: []string{ PERMISSION_CREATE_POST.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, }, SchemeManaged: false, BuiltIn: true, @@ -334,6 +339,7 @@ func MakeDefaultRoles() map[string]*Role { Description: "authentication.roles.system_post_all_public.description", Permissions: []string{ PERMISSION_CREATE_POST_PUBLIC.Id, + PERMISSION_USE_CHANNEL_MENTIONS.Id, }, SchemeManaged: false, BuiltIn: true,