diff --git a/keyserver/src/updaters/thread-updaters.js b/keyserver/src/updaters/thread-updaters.js --- a/keyserver/src/updaters/thread-updaters.js +++ b/keyserver/src/updaters/thread-updaters.js @@ -2,10 +2,7 @@ import { getRolePermissionBlobs } from 'lib/permissions/thread-permissions.js'; import { filteredThreadIDs } from 'lib/selectors/calendar-filter-selectors.js'; -import { - getPinnedContentFromMessage, - isInvalidPinSource, -} from 'lib/shared/message-utils.js'; +import { getPinnedContentFromMessage } from 'lib/shared/message-utils.js'; import { threadHasAdminRole, roleIsAdminRole, @@ -32,6 +29,7 @@ import { ServerError } from 'lib/utils/errors.js'; import { promiseAll } from 'lib/utils/promises.js'; import { firstLine } from 'lib/utils/string-utils.js'; +import { canToggleMessagePin } from 'lib/utils/toggle-pin-utils.js'; import { validChatNameRegex } from 'lib/utils/validation-utils.js'; import { reportLinkUsage } from './link-updaters.js'; @@ -50,6 +48,7 @@ fetchThreadInfos, fetchServerThreadInfos, determineThreadAncestry, + rawThreadInfosFromServerThreadInfos, } from '../fetchers/thread-fetchers.js'; import { checkThreadPermission, @@ -876,18 +875,23 @@ const { messageID, action } = request; const targetMessage = await fetchMessageInfoByID(viewer, messageID); - if (!targetMessage || isInvalidPinSource(targetMessage)) { + if (!targetMessage) { throw new ServerError('invalid_parameters'); } const { threadID } = targetMessage; - const hasPermission = await checkThreadPermission( - viewer, + const fetchServerThreadInfosResult = await fetchServerThreadInfos({ threadID, - threadPermissions.MANAGE_PINS, + }); + const { threadInfos: rawThreadInfos } = rawThreadInfosFromServerThreadInfos( + viewer, + fetchServerThreadInfosResult, ); - if (!hasPermission) { - throw new ServerError('invalid_credentials'); + const rawThreadInfo = rawThreadInfos[threadID]; + + const canTogglePin = canToggleMessagePin(targetMessage, rawThreadInfo); + if (!canTogglePin) { + throw new ServerError('invalid_parameters'); } const pinnedValue = action === 'pin' ? 1 : 0; @@ -929,9 +933,7 @@ }; const createUpdatesAsync = async () => { - const { threadInfos: serverThreadInfos } = await fetchServerThreadInfos({ - threadID, - }); + const { threadInfos: serverThreadInfos } = fetchServerThreadInfosResult; const time = Date.now(); const updates = []; for (const member of serverThreadInfos[threadID].members) { diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js --- a/lib/shared/message-utils.js +++ b/lib/shared/message-utils.js @@ -41,7 +41,7 @@ RawReactionMessageInfo, ReactionMessageInfo, } from '../types/messages/reaction.js'; -import type { ThreadInfo } from '../types/thread-types.js'; +import type { RawThreadInfo, ThreadInfo } from '../types/thread-types.js'; import type { UserInfos } from '../types/user-types.js'; import { type EntityText, @@ -673,7 +673,7 @@ function isInvalidPinSourceForThread( messageInfo: RawMessageInfo | MessageInfo, - threadInfo: ThreadInfo, + threadInfo: RawThreadInfo | ThreadInfo, ): boolean { const canBePinned = messageSpecs[messageInfo.type].canBePinned; const isFirstMessageInSidebar = threadInfo.sourceMessageID === messageInfo.id; diff --git a/lib/utils/toggle-pin-utils.js b/lib/utils/toggle-pin-utils.js --- a/lib/utils/toggle-pin-utils.js +++ b/lib/utils/toggle-pin-utils.js @@ -5,13 +5,17 @@ import type { ComposableMessageInfo, RobotextMessageInfo, + RawMessageInfo, } from '../types/message-types.js'; import { threadPermissions } from '../types/thread-permission-types.js'; -import type { ThreadInfo } from '../types/thread-types.js'; +import type { RawThreadInfo, ThreadInfo } from '../types/thread-types.js'; + +type ClientProvidedMessageInfo = ComposableMessageInfo | RobotextMessageInfo; +type ServerProvidedMessageInfo = RawMessageInfo; function canToggleMessagePin( - messageInfo: ComposableMessageInfo | RobotextMessageInfo, - threadInfo: ThreadInfo, + messageInfo: ClientProvidedMessageInfo | ServerProvidedMessageInfo, + threadInfo: RawThreadInfo | ThreadInfo, ): boolean { const isValidMessage = !isInvalidPinSourceForThread(messageInfo, threadInfo); const hasManagePinsPermission = threadHasPermission(