MM-62050: restrict channel bookmarks in archived channels (#29490)

This commit is contained in:
Caleb Roseland 2025-02-04 15:10:32 -06:00
parent d2e4eadb73
commit 436e15ec5f
4 changed files with 219 additions and 1 deletions

View File

@ -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

View File

@ -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,
@ -414,6 +429,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"),
@ -847,6 +892,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)
@ -1171,7 +1241,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)
@ -1584,6 +1679,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,

View File

@ -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."
@ -4350,6 +4366,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}}"

View File

@ -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') {