diff --git a/e2e-tests/cypress/tests/integration/channels/channel/channel_bookmarks_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel/channel_bookmarks_spec.ts index 412ebe5fe6..d2ae0f1921 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel/channel_bookmarks_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel/channel_bookmarks_spec.ts @@ -27,6 +27,7 @@ describe('Channel Bookmarks', () => { let admin: UserProfile; let publicChannel: Channel; let privateChannel: Channel; + let channelToArchive: Channel; const BOOKMARK_LIMIT = 50; @@ -46,6 +47,11 @@ describe('Channel Bookmarks', () => { cy.apiAddUserToChannel(privateChannel.id, user1.id); }); + cy.apiCreateChannel(testTeam.id, 'public-channel-archive', 'public channel archive', 'O').then((result) => { + channelToArchive = result.channel; + cy.apiAddUserToChannel(channelToArchive.id, user1.id); + }); + cy.visit(`/${testTeam.name}/channels/${publicChannel.name}`); }); }); @@ -297,6 +303,17 @@ describe('Channel Bookmarks', () => { verifyCanCreate(); }); + it('cannot add bookmark: archived channel', () => { + // # private channel + cy.visit(`/${testTeam.name}/channels/${channelToArchive.name}`); + createLinkBookmark({fromChannelMenu: true}); + + cy.apiDeleteChannel(channelToArchive.id); + + // * Verify cannot create in archived channel + verifyCannotCreate(); + }); + it('cannot add bookmark: private channel, non-admin', () => { // # Switch to non-admin user cy.apiLogin(user1); diff --git a/server/channels/api4/channel_bookmark.go b/server/channels/api4/channel_bookmark.go index 97559a46a4..4d800f989a 100644 --- a/server/channels/api4/channel_bookmark.go +++ b/server/channels/api4/channel_bookmark.go @@ -41,6 +41,11 @@ func createChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + if channel.DeleteAt != 0 { + c.Err = model.NewAppError("createChannelBookmark", "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", nil, "", http.StatusForbidden) + return + } + var channelBookmark *model.ChannelBookmark err := json.NewDecoder(r.Body).Decode(&channelBookmark) if err != nil || channelBookmark == nil { @@ -149,6 +154,11 @@ func updateChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + if channel.DeleteAt != 0 { + c.Err = model.NewAppError("updateChannelBookmark", "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", nil, "", http.StatusForbidden) + return + } + switch channel.Type { case model.ChannelTypeOpen: if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionEditBookmarkPublicChannel) { @@ -236,6 +246,11 @@ func updateChannelBookmarkSortOrder(c *Context, w http.ResponseWriter, r *http.R return } + if channel.DeleteAt != 0 { + c.Err = model.NewAppError("updateChannelBookmarkSortOrder", "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", nil, "", http.StatusForbidden) + return + } + switch channel.Type { case model.ChannelTypeOpen: if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionOrderBookmarkPublicChannel) { @@ -316,6 +331,11 @@ func deleteChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + if channel.DeleteAt != 0 { + c.Err = model.NewAppError("deleteChannelBookmark", "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", nil, "", http.StatusForbidden) + return + } + switch channel.Type { case model.ChannelTypeOpen: if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionDeleteBookmarkPublicChannel) { @@ -396,6 +416,14 @@ func listChannelBookmarksForChannel(c *Context, w http.ResponseWriter, r *http.R c.Err = appErr return } + + if !*c.App.Config().TeamSettings.ExperimentalViewArchivedChannels { + if channel.DeleteAt != 0 { + c.Err = model.NewAppError("listChannelBookmarksForChannel", "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", nil, "", http.StatusForbidden) + return + } + } + if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { c.SetPermissionError(model.PermissionReadChannelContent) return diff --git a/server/channels/api4/channel_bookmark_test.go b/server/channels/api4/channel_bookmark_test.go index 8b3902a339..8848df7f60 100644 --- a/server/channels/api4/channel_bookmark_test.go +++ b/server/channels/api4/channel_bookmark_test.go @@ -131,6 +131,21 @@ func TestCreateChannelBookmark(t *testing.T) { require.Nil(t, cb) }) + t.Run("bookmark creation should not work in an archived channel", func(t *testing.T) { + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + require.Nil(t, cb) + }) + t.Run("a guest user should not be able to create a channel bookmark", func(t *testing.T) { channelBookmark := &model.ChannelBookmark{ ChannelId: th.BasicChannel.Id, @@ -411,6 +426,36 @@ func TestEditChannelBookmark(t *testing.T) { require.Nil(t, ucb) }) + t.Run("bookmark editing should not work in an archived channel", func(t *testing.T) { + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + _, _, _ = th.SystemAdminClient.RestoreChannel(context.Background(), channelBookmark.ChannelId) + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + require.NotNil(t, cb) + + _, _ = th.SystemAdminClient.DeleteChannel(context.Background(), cb.ChannelId) + + // try to patch the channel bookmark + patch := &model.ChannelBookmarkPatch{ + DisplayName: model.NewPointer("Edited bookmark test"), + LinkUrl: model.NewPointer("http://edited.url"), + } + + ucb, resp, err := th.Client.UpdateChannelBookmark(context.Background(), cb.ChannelId, cb.Id, patch) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + require.Nil(t, ucb) + }) + t.Run("trying to edit a nonexistent bookmark should fail", func(t *testing.T) { patch := &model.ChannelBookmarkPatch{ DisplayName: model.NewPointer("Edited bookmark test"), @@ -839,6 +884,31 @@ func TestUpdateChannelBookmarkSortOrder(t *testing.T) { require.Nil(t, bookmarks) }) + t.Run("bookmark ordering should not work in an archived channel", func(t *testing.T) { + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + _, _, _ = th.SystemAdminClient.RestoreChannel(context.Background(), channelBookmark.ChannelId) + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + require.NotNil(t, cb) + + _, _ = th.SystemAdminClient.DeleteChannel(context.Background(), cb.ChannelId) + + // try to update the channel bookmark's order + bookmarks, resp, err := th.Client.UpdateChannelBookmarkSortOrder(context.Background(), cb.ChannelId, cb.Id, 0) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + require.Nil(t, bookmarks) + }) + t.Run("trying to update the order of a nonexistent bookmark should fail", func(t *testing.T) { bookmarks, resp, err := th.Client.UpdateChannelBookmarkSortOrder(context.Background(), th.BasicChannel.Id, model.NewId(), 1) require.Error(t, err) @@ -1158,7 +1228,32 @@ func TestDeleteChannelBookmark(t *testing.T) { th.PatchChannelModerationsForMembers(th.BasicChannel.Id, manageBookmarks, false) defer th.PatchChannelModerationsForMembers(th.BasicChannel.Id, manageBookmarks, true) - // try to delete the channel bookmark's order + // try to delete the channel bookmark + bookmarks, resp, err := th.Client.DeleteChannelBookmark(context.Background(), cb.ChannelId, cb.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + require.Nil(t, bookmarks) + }) + + t.Run("bookmark deletion should not work in an archived channel", func(t *testing.T) { + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + _, _, _ = th.SystemAdminClient.RestoreChannel(context.Background(), channelBookmark.ChannelId) + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + require.NotNil(t, cb) + + _, _ = th.SystemAdminClient.DeleteChannel(context.Background(), cb.ChannelId) + + // try to delete the channel bookmark bookmarks, resp, err := th.Client.DeleteChannelBookmark(context.Background(), cb.ChannelId, cb.Id) require.Error(t, err) CheckForbiddenStatus(t, resp) @@ -1567,6 +1662,76 @@ func TestListChannelBookmarksForChannel(t *testing.T) { } }) + t.Run("bookmark listing should not work in an archived channel without ExperimentalViewArchivedChannels", func(t *testing.T) { + experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels + }) + }() + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.ExperimentalViewArchivedChannels = false + }) + + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + _, _, _ = th.SystemAdminClient.RestoreChannel(context.Background(), channelBookmark.ChannelId) + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + require.NotNil(t, cb) + + _, _ = th.SystemAdminClient.DeleteChannel(context.Background(), cb.ChannelId) + + // try to list the channel bookmarks + bookmarks, resp, err := th.Client.ListChannelBookmarksForChannel(context.Background(), cb.ChannelId, 0) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + require.Nil(t, bookmarks) + }) + + t.Run("bookmark listing should work in an archived channel with ExperimentalViewArchivedChannels", func(t *testing.T) { + experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels + }) + }() + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.ExperimentalViewArchivedChannels = true + }) + + channelBookmark := &model.ChannelBookmark{ + ChannelId: th.BasicDeletedChannel.Id, + DisplayName: "Link bookmark test", + LinkUrl: "https://mattermost.com", + Type: model.ChannelBookmarkLink, + Emoji: ":smile:", + } + + _, _, _ = th.SystemAdminClient.RestoreChannel(context.Background(), channelBookmark.ChannelId) + + cb, resp, err := th.Client.CreateChannelBookmark(context.Background(), channelBookmark) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + require.NotNil(t, cb) + + _, _ = th.SystemAdminClient.DeleteChannel(context.Background(), cb.ChannelId) + + // try to list the channel bookmarks + bookmarks, resp, err := th.Client.ListChannelBookmarksForChannel(context.Background(), cb.ChannelId, 0) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.NotNil(t, bookmarks) + }) + t.Run("bookmark listing should work in a moderated channel", func(t *testing.T) { channelBookmark := &model.ChannelBookmark{ ChannelId: th.BasicChannel.Id, diff --git a/server/i18n/en.json b/server/i18n/en.json index 43cf0ce975..cffe871b7c 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -235,6 +235,10 @@ "id": "api.channel.bookmark.channel_bookmark.license.error", "translation": "Your license does not support channel bookmarks." }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Failed to create the channel bookmark." + }, { "id": "api.channel.bookmark.create_channel_bookmark.direct_or_group_channels.forbidden.app_error", "translation": "User is not allowed to create a channel bookmark." @@ -247,6 +251,10 @@ "id": "api.channel.bookmark.create_channel_bookmark.forbidden.app_error", "translation": "Failed to create the channel bookmark." }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Failed to delete the channel bookmark." + }, { "id": "api.channel.bookmark.delete_channel_bookmark.direct_or_group_channels.forbidden.app_error", "translation": "Failed to delete the channel bookmark." @@ -259,6 +267,10 @@ "id": "api.channel.bookmark.delete_channel_bookmark.forbidden.app_error", "translation": "Failed to delete the channel bookmark." }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Failed to update the channel bookmark." + }, { "id": "api.channel.bookmark.update_channel_bookmark.direct_or_group_channels.forbidden.app_error", "translation": "Failed to update the channel bookmark." @@ -271,6 +283,10 @@ "id": "api.channel.bookmark.update_channel_bookmark.forbidden.app_error", "translation": "Failed to update the channel bookmark." }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Failed to update the channel bookmark's sort order." + }, { "id": "api.channel.bookmark.update_channel_bookmark_sort_order.direct_or_group_channels.forbidden.app_error", "translation": "Failed to update the channel bookmark's sort order." @@ -4362,6 +4378,10 @@ "id": "api.user.view_archived_channels.get_users_in_channel.app_error", "translation": "Cannot retrieve users for an archived channel" }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Cannot retrieve bookmarks for an archived channel" + }, { "id": "api.web_socket.connect.upgrade.app_error", "translation": "URL Blocked because of CORS. Url: {{.BlockedOrigin}}" diff --git a/webapp/channels/src/components/channel_bookmarks/utils.ts b/webapp/channels/src/components/channel_bookmarks/utils.ts index 9c4325de8c..88d8569f84 100644 --- a/webapp/channels/src/components/channel_bookmarks/utils.ts +++ b/webapp/channels/src/components/channel_bookmarks/utils.ts @@ -57,6 +57,11 @@ export const getHaveIChannelBookmarkPermission = (state: GlobalState, channelId: if (!channel) { return false; } + + if (channel.delete_at !== 0) { + return false; + } + const {type} = channel; if (type === 'threads') {