diff --git a/keyserver/src/responders/activity-responders.js b/keyserver/src/responders/activity-responders.js --- a/keyserver/src/responders/activity-responders.js +++ b/keyserver/src/responders/activity-responders.js @@ -3,12 +3,14 @@ import t from 'tcomb'; import type { TList } from 'tcomb'; -import type { - UpdateActivityResult, - UpdateActivityRequest, - SetThreadUnreadStatusRequest, - SetThreadUnreadStatusResult, - ActivityUpdate, +import { + type UpdateActivityResult, + type UpdateActivityRequest, + type SetThreadUnreadStatusRequest, + type SetThreadUnreadStatusResult, + type ActivityUpdate, + setThreadUnreadStatusResult, + updateActivityResultValidator, } from 'lib/types/activity-types.js'; import { tShape } from 'lib/utils/validation-utils.js'; @@ -17,7 +19,7 @@ activityUpdater, setThreadUnreadStatus, } from '../updaters/activity-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const activityUpdatesInputValidator: TList> = t.list( tShape({ @@ -37,7 +39,8 @@ ): Promise { const request: UpdateActivityRequest = input; await validateInput(viewer, inputValidator, request); - return await activityUpdater(viewer, request); + const result = await activityUpdater(viewer, request); + return validateOutput(viewer, updateActivityResultValidator, result); } const setThreadUnreadStatusValidator = tShape({ @@ -52,7 +55,8 @@ const request: SetThreadUnreadStatusRequest = input; await validateInput(viewer, setThreadUnreadStatusValidator, request); - return await setThreadUnreadStatus(viewer, request); + const result = await setThreadUnreadStatus(viewer, request); + return validateOutput(viewer, setThreadUnreadStatusResult, result); } export { diff --git a/keyserver/src/responders/entry-responders.js b/keyserver/src/responders/entry-responders.js --- a/keyserver/src/responders/entry-responders.js +++ b/keyserver/src/responders/entry-responders.js @@ -46,7 +46,7 @@ compareNewCalendarQuery, } from '../updaters/entry-updaters.js'; import { commitSessionUpdate } from '../updaters/session-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; type EntryQueryInput = { +startDate: string, @@ -144,7 +144,10 @@ await verifyCalendarQueryThreadIDs(request); const response = await fetchEntryInfos(viewer, [request]); - return { ...response, userInfos: {} }; + return validateOutput(viewer, fetchEntryInfosResponseValidator, { + ...response, + userInfos: {}, + }); } const entryRevisionHistoryFetchInputValidator = tShape({ @@ -163,7 +166,12 @@ const request: FetchEntryRevisionInfosRequest = input; await validateInput(viewer, entryRevisionHistoryFetchInputValidator, request); const entryHistory = await fetchEntryRevisionInfo(viewer, request.id); - return { result: entryHistory }; + const response = { result: entryHistory }; + return validateOutput( + viewer, + fetchEntryRevisionInfosResultValidator, + response, + ); } const createEntryRequestInputValidator = tShape({ @@ -189,7 +197,8 @@ ): Promise { const request: CreateEntryRequest = input; await validateInput(viewer, createEntryRequestInputValidator, request); - return await createEntry(viewer, request); + const response = await createEntry(viewer, request); + return validateOutput(viewer, saveEntryResponseValidator, response); } const saveEntryRequestInputValidator = tShape({ @@ -207,7 +216,8 @@ ): Promise { const request: SaveEntryRequest = input; await validateInput(viewer, saveEntryRequestInputValidator, request); - return await updateEntry(viewer, request); + const response = await updateEntry(viewer, request); + return validateOutput(viewer, saveEntryResponseValidator, response); } const deleteEntryRequestInputValidator = tShape({ @@ -231,7 +241,8 @@ ): Promise { const request: DeleteEntryRequest = input; await validateInput(viewer, deleteEntryRequestInputValidator, request); - return await deleteEntry(viewer, request); + const response = await deleteEntry(viewer, request); + return validateOutput(viewer, deleteEntryResponseValidator, response); } const restoreEntryRequestInputValidator = tShape({ @@ -253,7 +264,8 @@ ): Promise { const request: RestoreEntryRequest = input; await validateInput(viewer, restoreEntryRequestInputValidator, request); - return await restoreEntry(viewer, request); + const response = await restoreEntry(viewer, request); + return validateOutput(viewer, restoreEntryResponseValidator, response); } export const deltaEntryInfosResultValidator: TInterface = @@ -283,12 +295,12 @@ commitSessionUpdate(viewer, sessionUpdate), ]); - return { + return validateOutput(viewer, deltaEntryInfosResultValidator, { rawEntryInfos: response.rawEntryInfos, deletedEntryIDs: response.deletedEntryIDs, // Old clients expect userInfos object userInfos: [], - }; + }); } export { diff --git a/keyserver/src/responders/keys-responders.js b/keyserver/src/responders/keys-responders.js --- a/keyserver/src/responders/keys-responders.js +++ b/keyserver/src/responders/keys-responders.js @@ -11,7 +11,7 @@ import { fetchSessionPublicKeys } from '../fetchers/key-fetchers.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const getSessionPublicKeysInputValidator = tShape({ session: t.String, @@ -30,7 +30,12 @@ } const request: GetSessionPublicKeysArgs = input; await validateInput(viewer, getSessionPublicKeysInputValidator, request); - return await fetchSessionPublicKeys(request.session); + const response = await fetchSessionPublicKeys(request.session); + return validateOutput( + viewer, + getSessionPublicKeysResponseValidator, + response, + ); } export { getSessionPublicKeysResponder }; diff --git a/keyserver/src/responders/message-report-responder.js b/keyserver/src/responders/message-report-responder.js --- a/keyserver/src/responders/message-report-responder.js +++ b/keyserver/src/responders/message-report-responder.js @@ -11,7 +11,7 @@ import createMessageReport from '../creators/message-report-creator.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const messageReportCreationRequestInputValidator = tShape({ messageID: t.String, @@ -32,7 +32,8 @@ const request: MessageReportCreationRequest = input; const rawMessageInfos = await createMessageReport(viewer, request); - return { messageInfo: rawMessageInfos[0] }; + const result = { messageInfo: rawMessageInfos[0] }; + return validateOutput(viewer, messageReportCreationResultValidator, result); } export { messageReportCreationResponder }; diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -60,7 +60,7 @@ assignImages, assignMessageContainerToMedia, } from '../updaters/upload-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const sendTextMessageRequestInputValidator = tShape({ threadID: t.String, @@ -118,7 +118,8 @@ } const rawMessageInfos = await createMessages(viewer, [messageData]); - return { newMessageInfo: rawMessageInfos[0] }; + const response = { newMessageInfo: rawMessageInfos[0] }; + return validateOutput(viewer, sendMessageResponseValidator, response); } const fetchMessageInfosRequestInputValidator = tShape({ @@ -144,7 +145,10 @@ { threadCursors: request.cursors }, request.numberPerThread ? request.numberPerThread : defaultNumberPerThread, ); - return { ...response, userInfos: {} }; + return validateOutput(viewer, fetchMessageInfosResponseValidator, { + ...response, + userInfos: {}, + }); } const sendMultimediaMessageRequestInputValidator = t.union([ @@ -239,7 +243,8 @@ ); } - return { newMessageInfo }; + const response = { newMessageInfo }; + return validateOutput(viewer, sendMessageResponseValidator, response); } const sendReactionMessageRequestInputValidator = tShape({ @@ -313,7 +318,8 @@ const rawMessageInfos = await createMessages(viewer, [messageData]); - return { newMessageInfo: rawMessageInfos[0] }; + const response = { newMessageInfo: rawMessageInfos[0] }; + return validateOutput(viewer, sendMessageResponseValidator, response); } const editMessageRequestInputValidator = tShape({ @@ -403,7 +409,8 @@ const newMessageInfos = await createMessages(viewer, messagesData); - return { newMessageInfos }; + const response = { newMessageInfos }; + return validateOutput(viewer, sendEditMessageResponseValidator, response); } const fetchPinnedMessagesResponderInputValidator = tShape({ @@ -425,7 +432,8 @@ fetchPinnedMessagesResponderInputValidator, input, ); - return await fetchPinnedMessageInfos(viewer, request); + const response = await fetchPinnedMessageInfos(viewer, request); + return validateOutput(viewer, fetchPinnedMessagesResultValidator, response); } export { diff --git a/keyserver/src/responders/relationship-responders.js b/keyserver/src/responders/relationship-responders.js --- a/keyserver/src/responders/relationship-responders.js +++ b/keyserver/src/responders/relationship-responders.js @@ -11,7 +11,7 @@ import type { Viewer } from '../session/viewer.js'; import { updateRelationships } from '../updaters/relationship-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const updateRelationshipInputValidator = tShape({ action: t.enums.of(relationshipActionsList, 'relationship action'), @@ -31,7 +31,8 @@ ): Promise { const request: RelationshipRequest = input; await validateInput(viewer, updateRelationshipInputValidator, request); - return await updateRelationships(viewer, request); + const response = await updateRelationships(viewer, request); + return validateOutput(viewer, relationshipErrorsValidator, response); } export { updateRelationshipsResponder }; diff --git a/keyserver/src/responders/report-responders.js b/keyserver/src/responders/report-responders.js --- a/keyserver/src/responders/report-responders.js +++ b/keyserver/src/responders/report-responders.js @@ -29,7 +29,7 @@ fetchReduxToolsImport, } from '../fetchers/report-fetchers.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const tActionSummary = tShape({ type: t.String, @@ -156,7 +156,7 @@ if (!response) { throw new ServerError('ignored_report'); } - return response; + return validateOutput(viewer, reportCreationResponseValidator, response); } const reportMultiCreationRequestInputValidator = tShape({ @@ -224,7 +224,12 @@ fetchErrorReportInfosRequestInputValidator, request, ); - return await fetchErrorReportInfos(viewer, request); + const response = await fetchErrorReportInfos(viewer, request); + return validateOutput( + viewer, + fetchErrorReportInfosResponseValidator, + response, + ); } async function errorReportDownloadResponder( diff --git a/keyserver/src/responders/search-responders.js b/keyserver/src/responders/search-responders.js --- a/keyserver/src/responders/search-responders.js +++ b/keyserver/src/responders/search-responders.js @@ -11,7 +11,7 @@ import { searchForUsers } from '../search/users.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const userSearchRequestInputValidator = tShape({ prefix: t.maybe(t.String), @@ -29,7 +29,8 @@ const request: UserSearchRequest = input; await validateInput(viewer, userSearchRequestInputValidator, request); const searchResults = await searchForUsers(request); - return { userInfos: searchResults }; + const result = { userInfos: searchResults }; + return validateOutput(viewer, userSearchResultValidator, result); } export { userSearchResponder }; diff --git a/keyserver/src/responders/thread-responders.js b/keyserver/src/responders/thread-responders.js --- a/keyserver/src/responders/thread-responders.js +++ b/keyserver/src/responders/thread-responders.js @@ -56,7 +56,7 @@ joinThread, toggleMessagePinForThread, } from '../updaters/thread-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const threadDeletionRequestInputValidator = tShape({ threadID: t.String, @@ -77,7 +77,8 @@ ): Promise { const request: ThreadDeletionRequest = input; await validateInput(viewer, threadDeletionRequestInputValidator, request); - return await deleteThread(viewer, request); + const result = await deleteThread(viewer, request); + return validateOutput(viewer, leaveThreadResultValidator, result); } const roleChangeRequestInputValidator = tShape({ @@ -105,7 +106,8 @@ ): Promise { const request: RoleChangeRequest = input; await validateInput(viewer, roleChangeRequestInputValidator, request); - return await updateRole(viewer, request); + const result = await updateRole(viewer, request); + return validateOutput(viewer, changeThreadSettingsResultValidator, result); } const removeMembersRequestInputValidator = tShape({ @@ -119,7 +121,8 @@ ): Promise { const request: RemoveMembersRequest = input; await validateInput(viewer, removeMembersRequestInputValidator, request); - return await removeMembers(viewer, request); + const result = await removeMembers(viewer, request); + return validateOutput(viewer, changeThreadSettingsResultValidator, result); } const leaveThreadRequestInputValidator = tShape({ @@ -132,7 +135,8 @@ ): Promise { const request: LeaveThreadRequest = input; await validateInput(viewer, leaveThreadRequestInputValidator, request); - return await leaveThread(viewer, request); + const result = await leaveThread(viewer, request); + return validateOutput(viewer, leaveThreadResultValidator, result); } const updateThreadRequestInputValidator = tShape({ @@ -155,7 +159,8 @@ ): Promise { const request: UpdateThreadRequest = input; await validateInput(viewer, updateThreadRequestInputValidator, request); - return await updateThread(viewer, request); + const result = await updateThread(viewer, request); + return validateOutput(viewer, changeThreadSettingsResultValidator, result); } const threadRequestValidationShape = { @@ -201,9 +206,10 @@ const request: ServerNewThreadRequest = input; await validateInput(viewer, newThreadRequestInputValidator, request); - return await createThread(viewer, request, { + const result = await createThread(viewer, request, { silentlyFailMembers: request.type === threadTypes.SIDEBAR, }); + return validateOutput(viewer, newThreadResponseValidator, result); } const joinThreadRequestInputValidator = tShape({ @@ -235,7 +241,8 @@ await verifyCalendarQueryThreadIDs(request.calendarQuery); } - return await joinThread(viewer, request); + const result = await joinThread(viewer, request); + return validateOutput(viewer, threadJoinResultValidator, result); } const threadFetchMediaRequestInputValidator = tShape({ @@ -253,7 +260,8 @@ ): Promise { const request: ThreadFetchMediaRequest = input; await validateInput(viewer, threadFetchMediaRequestInputValidator, request); - return await fetchMediaForThread(viewer, request); + const result = await fetchMediaForThread(viewer, request); + return validateOutput(viewer, threadFetchMediaResultValidator, result); } const toggleMessagePinRequestInputValidator = tShape({ @@ -273,7 +281,8 @@ ): Promise { const request: ToggleMessagePinRequest = input; await validateInput(viewer, toggleMessagePinRequestInputValidator, request); - return await toggleMessagePinForThread(viewer, request); + const result = await toggleMessagePinForThread(viewer, request); + return validateOutput(viewer, toggleMessagePinResultValidator, result); } export { diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -29,10 +29,11 @@ notificationTypeValues, logInActionSources, } from 'lib/types/account-types.js'; -import type { - ClientAvatar, - UpdateUserAvatarRequest, - UpdateUserAvatarResponse, +import { + type ClientAvatar, + clientAvatarValidator, + type UpdateUserAvatarRequest, + type UpdateUserAvatarResponse, } from 'lib/types/avatar-types.js'; import type { IdentityKeysBlob, @@ -58,6 +59,7 @@ threadSubscriptionValidator, } from 'lib/types/subscription-types.js'; import { rawThreadInfoValidator } from 'lib/types/thread-types.js'; +import { createUpdatesResultValidator } from 'lib/types/update-types.js'; import { type PasswordUpdate, loggedOutUserInfoValidator, @@ -129,7 +131,7 @@ import { userSubscriptionUpdater } from '../updaters/user-subscription-updaters.js'; import { viewerAcknowledgmentUpdater } from '../updaters/viewer-acknowledgment-updater.js'; import { getOlmUtility } from '../utils/olm-utils.js'; -import { validateInput } from '../utils/validation-utils.js'; +import { validateInput, validateOutput } from '../utils/validation-utils.js'; const subscriptionUpdateRequestInputValidator = tShape({ threadID: t.String, @@ -151,7 +153,9 @@ const request: SubscriptionUpdateRequest = input; await validateInput(viewer, subscriptionUpdateRequestInputValidator, request); const threadSubscription = await userSubscriptionUpdater(viewer, request); - return { threadSubscription }; + return validateOutput(viewer, subscriptionUpdateResponseValidator, { + threadSubscription, + }); } const accountUpdateInputValidator = tShape({ @@ -206,12 +210,13 @@ ]); viewer.setNewCookie(anonymousViewerData); } - return { + const response = { currentUserInfo: { id: viewer.id, anonymous: true, }, }; + return validateOutput(viewer, logOutResponseValidator, response); } const deleteAccountRequestInputValidator = tShape({ @@ -226,7 +231,7 @@ await validateInput(viewer, deleteAccountRequestInputValidator, request); const result = await deleteAccount(viewer, request); invariant(result, 'deleteAccount should return result if handed request'); - return result; + return validateOutput(viewer, logOutResponseValidator, result); } const deviceTokenUpdateRequestInputValidator = tShape({ @@ -287,7 +292,8 @@ throw new ServerError('invalid_signature'); } } - return await createAccount(viewer, request); + const response = await createAccount(viewer, request); + return validateOutput(viewer, registerResponseValidator, response); } type ProcessSuccessfulLoginParams = { @@ -492,13 +498,14 @@ const id = userRow.id.toString(); - return await processSuccessfulLogin({ + const response = await processSuccessfulLogin({ viewer, input, userID: id, calendarQuery, signedIdentityKeysBlob, }); + return validateOutput(viewer, logInResponseValidator, response); } const siweAuthRequestInputValidator = tShape({ @@ -626,7 +633,7 @@ } // 9. Complete login with call to `processSuccessfulLogin(...)`. - return await processSuccessfulLogin({ + const response = await processSuccessfulLogin({ viewer, input, userID, @@ -634,6 +641,7 @@ socialProof, signedIdentityKeysBlob, }); + return validateOutput(viewer, logInResponseValidator, response); } const updatePasswordRequestInputValidator = tShape({ @@ -654,7 +662,8 @@ if (request.calendarQuery) { request.calendarQuery = normalizeCalendarQuery(request.calendarQuery); } - return await updatePassword(viewer, request); + const response = await updatePassword(viewer, request); + return validateOutput(viewer, logInResponseValidator, response); } const updateUserSettingsInputValidator = tShape({ @@ -671,7 +680,7 @@ ): Promise { const request: UpdateUserSettingsRequest = input; await validateInput(viewer, updateUserSettingsInputValidator, request); - return await updateUserSettings(viewer, request); + await updateUserSettings(viewer, request); } const policyAcknowledgmentRequestInputValidator = tShape({ @@ -691,13 +700,24 @@ await viewerAcknowledgmentUpdater(viewer, request.policy); } +export const updateUserAvatarResponseValidator: TInterface = + tShape({ + updates: createUpdatesResultValidator, + }); + +const updateUserAvatarResponderValidator = t.union([ + t.maybe(clientAvatarValidator), + updateUserAvatarResponseValidator, +]); + async function updateUserAvatarResponder( viewer: Viewer, input: any, ): Promise { const request: UpdateUserAvatarRequest = input; await validateInput(viewer, updateUserAvatarRequestValidator, request); - return await updateUserAvatar(viewer, request); + const result = await updateUserAvatar(viewer, request); + return validateOutput(viewer, updateUserAvatarResponderValidator, result); } export { diff --git a/lib/types/update-types.js b/lib/types/update-types.js --- a/lib/types/update-types.js +++ b/lib/types/update-types.js @@ -15,6 +15,7 @@ type UserInfo, userInfoValidator, type UserInfos, + userInfosValidator, type LoggedInUserInfo, loggedInUserInfoValidator, type OldLoggedInUserInfo, @@ -395,6 +396,11 @@ +viewerUpdates: $ReadOnlyArray, +userInfos: UserInfos, }; +export const createUpdatesResultValidator: TInterface = + tShape({ + viewerUpdates: t.list(serverUpdateInfoValidator), + userInfos: userInfosValidator, + }); export type ServerCreateUpdatesResponse = { +viewerUpdates: $ReadOnlyArray,