From 7492050ea7d9c27bb0a9a2f5f662eb8a69cf8f41 Mon Sep 17 00:00:00 2001 From: Michal Gniadek Date: Fri, 28 Jul 2023 19:02:52 +0200 Subject: [PATCH] [keyserver] Move validators to endpoint array Summary: (ENG-3879)[https://linear.app/comm/issue/ENG-3879/move-output-and-input-validators-outside-of-the-responders] Followup to the id schema work. Moving all the validation to the endpoint array makes it less likely for the devs to forgot to add it (which already happened a few times). Additionaly it moves the validation logic outside of the responder and now they are fully focused on the business logic. An additional change that needed to be made was exporting the input validators, so they could be imported in the `endpoints.js`. I choose to not split it into multiple diffs, because I'm doing the same thing everywhere, but if it's hard to review I can split into more diffs. Test Plan: - run `yarn flow-all` (note: for some reason flow allows wrong output responders, even they are correctly typed with generics) - play around the app and check if everything works correctly (in particular I tested log in, register and siwe flows) Reviewers: kamil, tomek Reviewed By: tomek Subscribers: ashoat Differential Revision: https://phab.comm.dev/D8661 --- keyserver/src/endpoints.js | 724 ++++++++++++------ .../src/responders/activity-responders.js | 49 +- keyserver/src/responders/device-responders.js | 7 +- keyserver/src/responders/entry-responders.js | 217 ++---- keyserver/src/responders/handlers.js | 28 +- keyserver/src/responders/keys-responders.js | 24 +- keyserver/src/responders/link-responders.js | 58 +- .../responders/message-report-responder.js | 18 +- .../src/responders/message-responders.js | 159 ++-- .../src/responders/relationship-responders.js | 24 +- keyserver/src/responders/report-responders.js | 92 +-- .../responders/responder-validators.test.js | 10 +- keyserver/src/responders/search-responders.js | 50 +- .../src/responders/siwe-nonce-responders.js | 8 +- keyserver/src/responders/thread-responders.js | 251 ++---- keyserver/src/responders/user-responders.js | 295 +++---- .../src/responders/verification-responders.js | 15 +- .../src/responders/version-responders.js | 13 +- keyserver/src/uploads/uploads.js | 49 +- lib/types/activity-types.js | 2 +- lib/types/crypto-types.js | 9 + lib/types/request-types.js | 15 +- 22 files changed, 961 insertions(+), 1156 deletions(-) diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js index 438a76273b..6a3c02d35a 100644 --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -1,13 +1,28 @@ // @flow +import t from 'tcomb'; + import { baseLegalPolicies } from 'lib/facts/policies.js'; +import { + setThreadUnreadStatusResultValidator, + updateActivityResultValidator, +} from 'lib/types/activity-types.js'; import type { Endpoint } from 'lib/types/endpoints.js'; +import { inviteLinkValidator } from 'lib/types/link-types.js'; +import { uploadMultimediaResultValidator } from 'lib/types/media-types.js'; +import { getOlmSessionInitializationDataResponseValidator } from 'lib/types/request-types.js'; +import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js'; import { updateActivityResponder, threadSetUnreadStatusResponder, + setThreadUnreadStatusValidator, + updateActivityResponderInputValidator, } from './responders/activity-responders.js'; -import { deviceTokenUpdateResponder } from './responders/device-responders.js'; +import { + deviceTokenUpdateResponder, + deviceTokenUpdateRequestInputValidator, +} from './responders/device-responders.js'; import { entryFetchResponder, entryRevisionFetchResponder, @@ -16,19 +31,44 @@ import { entryDeletionResponder, entryRestorationResponder, calendarQueryUpdateResponder, + createEntryRequestInputValidator, + saveEntryResponseValidator, + deleteEntryRequestInputValidator, + deleteEntryResponseValidator, + entryQueryInputValidator, + entryRevisionHistoryFetchInputValidator, + fetchEntryInfosResponseValidator, + fetchEntryRevisionInfosResultValidator, + deltaEntryInfosResultValidator, + newEntryQueryInputValidator, + restoreEntryRequestInputValidator, + restoreEntryResponseValidator, + saveEntryRequestInputValidator, } from './responders/entry-responders.js'; import type { JSONResponder } from './responders/handlers.js'; +import { createJSONResponder } from './responders/handlers.js'; import { getSessionPublicKeysResponder, getOlmSessionInitializationDataResponder, + getSessionPublicKeysInputValidator, + getSessionPublicKeysResponseValidator, } from './responders/keys-responders.js'; import { createOrUpdatePublicLinkResponder, disableInviteLinkResponder, fetchPrimaryInviteLinksResponder, inviteLinkVerificationResponder, + createOrUpdatePublicLinkInputValidator, + disableInviteLinkInputValidator, + fetchInviteLinksResponseValidator, + inviteLinkVerificationRequestInputValidator, + inviteLinkVerificationResponseValidator, } from './responders/link-responders.js'; -import { messageReportCreationResponder } from './responders/message-report-responder.js'; +import { + messageReportCreationResponder, + messageReportCreationRequestInputValidator, + messageReportCreationResultValidator, +} from './responders/message-report-responder.js'; import { textMessageCreationResponder, messageFetchResponder, @@ -37,18 +77,46 @@ import { editMessageCreationResponder, fetchPinnedMessagesResponder, searchMessagesResponder, + sendMessageResponseValidator, + sendMultimediaMessageRequestInputValidator, + sendReactionMessageRequestInputValidator, + editMessageRequestInputValidator, + sendEditMessageResponseValidator, + sendTextMessageRequestInputValidator, + fetchMessageInfosRequestInputValidator, + fetchMessageInfosResponseValidator, + fetchPinnedMessagesResponderInputValidator, + fetchPinnedMessagesResultValidator, + searchMessagesResponderInputValidator, + searchMessagesResponseValidator, } from './responders/message-responders.js'; -import { updateRelationshipsResponder } from './responders/relationship-responders.js'; +import { + updateRelationshipsResponder, + relationshipErrorsValidator, + updateRelationshipInputValidator, +} from './responders/relationship-responders.js'; import { reportCreationResponder, reportMultiCreationResponder, errorReportFetchInfosResponder, + reportCreationRequestInputValidator, + reportCreationResponseValidator, + fetchErrorReportInfosRequestInputValidator, + fetchErrorReportInfosResponseValidator, + reportMultiCreationRequestInputValidator, } from './responders/report-responders.js'; import { userSearchResponder, exactUserSearchResponder, + exactUserSearchRequestInputValidator, + exactUserSearchResultValidator, + userSearchRequestInputValidator, + userSearchResultValidator, } from './responders/search-responders.js'; -import { siweNonceResponder } from './responders/siwe-nonce-responders.js'; +import { + siweNonceResponder, + siweNonceResponseValidator, +} from './responders/siwe-nonce-responders.js'; import { threadDeletionResponder, roleUpdateResponder, @@ -61,6 +129,25 @@ import { toggleMessagePinResponder, roleModificationResponder, roleDeletionResponder, + leaveThreadResultValidator, + newThreadRequestInputValidator, + newThreadResponseValidator, + threadDeletionRequestInputValidator, + joinThreadRequestInputValidator, + leaveThreadRequestInputValidator, + threadFetchMediaRequestInputValidator, + threadFetchMediaResultValidator, + threadJoinResultValidator, + changeThreadSettingsResultValidator, + removeMembersRequestInputValidator, + roleChangeRequestInputValidator, + toggleMessagePinRequestInputValidator, + toggleMessagePinResultValidator, + updateThreadRequestInputValidator, + roleDeletionRequestInputValidator, + roleDeletionResultValidator, + roleModificationRequestInputValidator, + roleModificationResultValidator, } from './responders/thread-responders.js'; import { userSubscriptionUpdateResponder, @@ -76,255 +163,400 @@ import { updateUserSettingsResponder, policyAcknowledgmentResponder, updateUserAvatarResponder, + registerRequestInputValidator, + registerResponseValidator, + deleteAccountRequestInputValidator, + logOutResponseValidator, + logInRequestInputValidator, + logInResponseValidator, + policyAcknowledgmentRequestInputValidator, + accountUpdateInputValidator, + resetPasswordRequestInputValidator, + siweAuthRequestInputValidator, + subscriptionUpdateRequestInputValidator, + subscriptionUpdateResponseValidator, + updatePasswordRequestInputValidator, + updateUserAvatarResponderValidator, + updateUserSettingsInputValidator, } from './responders/user-responders.js'; -import { codeVerificationResponder } from './responders/verification-responders.js'; -import { versionResponder } from './responders/version-responders.js'; +import { + codeVerificationResponder, + codeVerificationRequestInputValidator, +} from './responders/verification-responders.js'; +import { + versionResponder, + versionResponseValidator, +} from './responders/version-responders.js'; import { uploadMediaMetadataResponder, uploadDeletionResponder, + UploadDeletionRequestInputValidator, + uploadMediaMetadataInputValidator, } from './uploads/uploads.js'; +const ignoredArgumentValidator = t.irreducible('Ignored argument', () => true); + const jsonEndpoints: { [id: Endpoint]: JSONResponder } = { - create_account: { - responder: accountCreationResponder, - requiredPolicies: [], - }, - create_entry: { - responder: entryCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - create_error_report: { - responder: reportCreationResponder, - requiredPolicies: [], - }, - create_message_report: { - responder: messageReportCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - create_multimedia_message: { - responder: multimediaMessageCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - create_or_update_public_link: { - responder: createOrUpdatePublicLinkResponder, - requiredPolicies: baseLegalPolicies, - }, - create_reaction_message: { - responder: reactionMessageCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - disable_invite_link: { - responder: disableInviteLinkResponder, - requiredPolicies: baseLegalPolicies, - }, - edit_message: { - responder: editMessageCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - create_report: { - responder: reportCreationResponder, - requiredPolicies: [], - }, - create_reports: { - responder: reportMultiCreationResponder, - requiredPolicies: [], - }, - create_text_message: { - responder: textMessageCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - create_thread: { - responder: threadCreationResponder, - requiredPolicies: baseLegalPolicies, - }, - delete_account: { - responder: accountDeletionResponder, - requiredPolicies: [], - }, - delete_entry: { - responder: entryDeletionResponder, - requiredPolicies: baseLegalPolicies, - }, - delete_community_role: { - responder: roleDeletionResponder, - requiredPolicies: baseLegalPolicies, - }, - delete_thread: { - responder: threadDeletionResponder, - requiredPolicies: baseLegalPolicies, - }, - delete_upload: { - responder: uploadDeletionResponder, - requiredPolicies: baseLegalPolicies, - }, - exact_search_user: { - responder: exactUserSearchResponder, - requiredPolicies: [], - }, - fetch_entries: { - responder: entryFetchResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_entry_revisions: { - responder: entryRevisionFetchResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_error_report_infos: { - responder: errorReportFetchInfosResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_messages: { - responder: messageFetchResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_pinned_messages: { - responder: fetchPinnedMessagesResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_primary_invite_links: { - responder: fetchPrimaryInviteLinksResponder, - requiredPolicies: baseLegalPolicies, - }, - fetch_thread_media: { - responder: threadFetchMediaResponder, - requiredPolicies: baseLegalPolicies, - }, - get_session_public_keys: { - responder: getSessionPublicKeysResponder, - requiredPolicies: baseLegalPolicies, - }, - join_thread: { - responder: threadJoinResponder, - requiredPolicies: baseLegalPolicies, - }, - leave_thread: { - responder: threadLeaveResponder, - requiredPolicies: baseLegalPolicies, - }, - log_in: { - responder: logInResponder, - requiredPolicies: [], - }, - log_out: { - responder: logOutResponder, - requiredPolicies: [], - }, - modify_community_role: { - responder: roleModificationResponder, - requiredPolicies: baseLegalPolicies, - }, - policy_acknowledgment: { - responder: policyAcknowledgmentResponder, - requiredPolicies: [], - }, - remove_members: { - responder: memberRemovalResponder, - requiredPolicies: baseLegalPolicies, - }, - restore_entry: { - responder: entryRestorationResponder, - requiredPolicies: baseLegalPolicies, - }, - search_messages: { - responder: searchMessagesResponder, - requiredPolicies: baseLegalPolicies, - }, - search_users: { - responder: userSearchResponder, - requiredPolicies: baseLegalPolicies, - }, - send_password_reset_email: { - responder: sendPasswordResetEmailResponder, - requiredPolicies: [], - }, - send_verification_email: { - responder: sendVerificationEmailResponder, - requiredPolicies: [], - }, - set_thread_unread_status: { - responder: threadSetUnreadStatusResponder, - requiredPolicies: baseLegalPolicies, - }, - toggle_message_pin: { - responder: toggleMessagePinResponder, - requiredPolicies: baseLegalPolicies, - }, - update_account: { - responder: passwordUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_activity: { - responder: updateActivityResponder, - requiredPolicies: baseLegalPolicies, - }, - update_calendar_query: { - responder: calendarQueryUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_user_settings: { - responder: updateUserSettingsResponder, - requiredPolicies: baseLegalPolicies, - }, - update_device_token: { - responder: deviceTokenUpdateResponder, - requiredPolicies: [], - }, - update_entry: { - responder: entryUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_password: { - responder: oldPasswordUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_relationships: { - responder: updateRelationshipsResponder, - requiredPolicies: baseLegalPolicies, - }, - update_role: { - responder: roleUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_thread: { - responder: threadUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - update_user_subscription: { - responder: userSubscriptionUpdateResponder, - requiredPolicies: baseLegalPolicies, - }, - verify_code: { - responder: codeVerificationResponder, - requiredPolicies: baseLegalPolicies, - }, - verify_invite_link: { - responder: inviteLinkVerificationResponder, - requiredPolicies: baseLegalPolicies, - }, - siwe_nonce: { - responder: siweNonceResponder, - requiredPolicies: [], - }, - siwe_auth: { - responder: siweAuthResponder, - requiredPolicies: [], - }, - update_user_avatar: { - responder: updateUserAvatarResponder, - requiredPolicies: baseLegalPolicies, - }, - upload_media_metadata: { - responder: uploadMediaMetadataResponder, - requiredPolicies: baseLegalPolicies, - }, - get_olm_session_initialization_data: { - responder: getOlmSessionInitializationDataResponder, - requiredPolicies: [], - }, - version: { - responder: versionResponder, - requiredPolicies: [], - }, + create_account: createJSONResponder( + accountCreationResponder, + registerRequestInputValidator, + registerResponseValidator, + [], + ), + create_entry: createJSONResponder( + entryCreationResponder, + createEntryRequestInputValidator, + saveEntryResponseValidator, + baseLegalPolicies, + ), + create_error_report: createJSONResponder( + reportCreationResponder, + reportCreationRequestInputValidator, + reportCreationResponseValidator, + [], + ), + create_message_report: createJSONResponder( + messageReportCreationResponder, + messageReportCreationRequestInputValidator, + messageReportCreationResultValidator, + baseLegalPolicies, + ), + create_multimedia_message: createJSONResponder( + multimediaMessageCreationResponder, + sendMultimediaMessageRequestInputValidator, + sendMessageResponseValidator, + baseLegalPolicies, + ), + create_or_update_public_link: createJSONResponder( + createOrUpdatePublicLinkResponder, + createOrUpdatePublicLinkInputValidator, + inviteLinkValidator, + baseLegalPolicies, + ), + create_reaction_message: createJSONResponder( + reactionMessageCreationResponder, + sendReactionMessageRequestInputValidator, + sendMessageResponseValidator, + baseLegalPolicies, + ), + disable_invite_link: createJSONResponder( + disableInviteLinkResponder, + disableInviteLinkInputValidator, + t.Nil, + baseLegalPolicies, + ), + edit_message: createJSONResponder( + editMessageCreationResponder, + editMessageRequestInputValidator, + sendEditMessageResponseValidator, + baseLegalPolicies, + ), + create_report: createJSONResponder( + reportCreationResponder, + reportCreationRequestInputValidator, + reportCreationResponseValidator, + [], + ), + create_reports: createJSONResponder( + reportMultiCreationResponder, + reportMultiCreationRequestInputValidator, + t.Nil, + [], + ), + create_text_message: createJSONResponder( + textMessageCreationResponder, + sendTextMessageRequestInputValidator, + sendMessageResponseValidator, + baseLegalPolicies, + ), + create_thread: createJSONResponder( + threadCreationResponder, + newThreadRequestInputValidator, + newThreadResponseValidator, + baseLegalPolicies, + ), + delete_account: createJSONResponder( + accountDeletionResponder, + deleteAccountRequestInputValidator, + logOutResponseValidator, + [], + ), + delete_entry: createJSONResponder( + entryDeletionResponder, + deleteEntryRequestInputValidator, + deleteEntryResponseValidator, + baseLegalPolicies, + ), + delete_community_role: createJSONResponder( + roleDeletionResponder, + roleDeletionRequestInputValidator, + roleDeletionResultValidator, + baseLegalPolicies, + ), + delete_thread: createJSONResponder( + threadDeletionResponder, + threadDeletionRequestInputValidator, + leaveThreadResultValidator, + baseLegalPolicies, + ), + delete_upload: createJSONResponder( + uploadDeletionResponder, + UploadDeletionRequestInputValidator, + t.Nil, + baseLegalPolicies, + ), + exact_search_user: createJSONResponder( + exactUserSearchResponder, + exactUserSearchRequestInputValidator, + exactUserSearchResultValidator, + [], + ), + fetch_entries: createJSONResponder( + entryFetchResponder, + entryQueryInputValidator, + fetchEntryInfosResponseValidator, + baseLegalPolicies, + ), + fetch_entry_revisions: createJSONResponder( + entryRevisionFetchResponder, + entryRevisionHistoryFetchInputValidator, + fetchEntryRevisionInfosResultValidator, + baseLegalPolicies, + ), + fetch_error_report_infos: createJSONResponder( + errorReportFetchInfosResponder, + fetchErrorReportInfosRequestInputValidator, + fetchErrorReportInfosResponseValidator, + baseLegalPolicies, + ), + fetch_messages: createJSONResponder( + messageFetchResponder, + fetchMessageInfosRequestInputValidator, + fetchMessageInfosResponseValidator, + baseLegalPolicies, + ), + fetch_pinned_messages: createJSONResponder( + fetchPinnedMessagesResponder, + fetchPinnedMessagesResponderInputValidator, + fetchPinnedMessagesResultValidator, + baseLegalPolicies, + ), + fetch_primary_invite_links: createJSONResponder( + fetchPrimaryInviteLinksResponder, + ignoredArgumentValidator, + fetchInviteLinksResponseValidator, + baseLegalPolicies, + ), + fetch_thread_media: createJSONResponder( + threadFetchMediaResponder, + threadFetchMediaRequestInputValidator, + threadFetchMediaResultValidator, + baseLegalPolicies, + ), + get_session_public_keys: createJSONResponder( + getSessionPublicKeysResponder, + getSessionPublicKeysInputValidator, + getSessionPublicKeysResponseValidator, + baseLegalPolicies, + ), + join_thread: createJSONResponder( + threadJoinResponder, + joinThreadRequestInputValidator, + threadJoinResultValidator, + baseLegalPolicies, + ), + leave_thread: createJSONResponder( + threadLeaveResponder, + leaveThreadRequestInputValidator, + leaveThreadResultValidator, + baseLegalPolicies, + ), + log_in: createJSONResponder( + logInResponder, + logInRequestInputValidator, + logInResponseValidator, + [], + ), + log_out: createJSONResponder( + logOutResponder, + ignoredArgumentValidator, + logOutResponseValidator, + [], + ), + modify_community_role: createJSONResponder( + roleModificationResponder, + roleModificationRequestInputValidator, + roleModificationResultValidator, + baseLegalPolicies, + ), + policy_acknowledgment: createJSONResponder( + policyAcknowledgmentResponder, + policyAcknowledgmentRequestInputValidator, + t.Nil, + [], + ), + remove_members: createJSONResponder( + memberRemovalResponder, + removeMembersRequestInputValidator, + changeThreadSettingsResultValidator, + baseLegalPolicies, + ), + restore_entry: createJSONResponder( + entryRestorationResponder, + restoreEntryRequestInputValidator, + restoreEntryResponseValidator, + baseLegalPolicies, + ), + search_messages: createJSONResponder( + searchMessagesResponder, + searchMessagesResponderInputValidator, + searchMessagesResponseValidator, + baseLegalPolicies, + ), + search_users: createJSONResponder( + userSearchResponder, + userSearchRequestInputValidator, + userSearchResultValidator, + baseLegalPolicies, + ), + send_password_reset_email: createJSONResponder( + sendPasswordResetEmailResponder, + resetPasswordRequestInputValidator, + t.Nil, + [], + ), + send_verification_email: createJSONResponder( + sendVerificationEmailResponder, + ignoredArgumentValidator, + t.Nil, + [], + ), + set_thread_unread_status: createJSONResponder( + threadSetUnreadStatusResponder, + setThreadUnreadStatusValidator, + setThreadUnreadStatusResultValidator, + baseLegalPolicies, + ), + toggle_message_pin: createJSONResponder( + toggleMessagePinResponder, + toggleMessagePinRequestInputValidator, + toggleMessagePinResultValidator, + baseLegalPolicies, + ), + update_account: createJSONResponder( + passwordUpdateResponder, + accountUpdateInputValidator, + t.Nil, + baseLegalPolicies, + ), + update_activity: createJSONResponder( + updateActivityResponder, + updateActivityResponderInputValidator, + updateActivityResultValidator, + baseLegalPolicies, + ), + update_calendar_query: createJSONResponder( + calendarQueryUpdateResponder, + newEntryQueryInputValidator, + deltaEntryInfosResultValidator, + baseLegalPolicies, + ), + update_user_settings: createJSONResponder( + updateUserSettingsResponder, + updateUserSettingsInputValidator, + t.Nil, + baseLegalPolicies, + ), + update_device_token: createJSONResponder( + deviceTokenUpdateResponder, + deviceTokenUpdateRequestInputValidator, + t.Nil, + [], + ), + update_entry: createJSONResponder( + entryUpdateResponder, + saveEntryRequestInputValidator, + saveEntryResponseValidator, + baseLegalPolicies, + ), + update_password: createJSONResponder( + oldPasswordUpdateResponder, + updatePasswordRequestInputValidator, + logInResponseValidator, + baseLegalPolicies, + ), + update_relationships: createJSONResponder( + updateRelationshipsResponder, + updateRelationshipInputValidator, + relationshipErrorsValidator, + baseLegalPolicies, + ), + update_role: createJSONResponder( + roleUpdateResponder, + roleChangeRequestInputValidator, + changeThreadSettingsResultValidator, + baseLegalPolicies, + ), + update_thread: createJSONResponder( + threadUpdateResponder, + updateThreadRequestInputValidator, + changeThreadSettingsResultValidator, + baseLegalPolicies, + ), + update_user_subscription: createJSONResponder( + userSubscriptionUpdateResponder, + subscriptionUpdateRequestInputValidator, + subscriptionUpdateResponseValidator, + baseLegalPolicies, + ), + verify_code: createJSONResponder( + codeVerificationResponder, + codeVerificationRequestInputValidator, + t.Nil, + baseLegalPolicies, + ), + verify_invite_link: createJSONResponder( + inviteLinkVerificationResponder, + inviteLinkVerificationRequestInputValidator, + inviteLinkVerificationResponseValidator, + baseLegalPolicies, + ), + siwe_nonce: createJSONResponder( + siweNonceResponder, + ignoredArgumentValidator, + siweNonceResponseValidator, + [], + ), + siwe_auth: createJSONResponder( + siweAuthResponder, + siweAuthRequestInputValidator, + logInResponseValidator, + [], + ), + update_user_avatar: createJSONResponder( + updateUserAvatarResponder, + updateUserAvatarRequestValidator, + updateUserAvatarResponderValidator, + baseLegalPolicies, + ), + upload_media_metadata: createJSONResponder( + uploadMediaMetadataResponder, + uploadMediaMetadataInputValidator, + uploadMultimediaResultValidator, + baseLegalPolicies, + ), + get_olm_session_initialization_data: createJSONResponder( + getOlmSessionInitializationDataResponder, + ignoredArgumentValidator, + getOlmSessionInitializationDataResponseValidator, + [], + ), + version: createJSONResponder( + versionResponder, + ignoredArgumentValidator, + versionResponseValidator, + [], + ), }; export { jsonEndpoints }; diff --git a/keyserver/src/responders/activity-responders.js b/keyserver/src/responders/activity-responders.js index 42758d7d28..f8d9f22276 100644 --- a/keyserver/src/responders/activity-responders.js +++ b/keyserver/src/responders/activity-responders.js @@ -1,7 +1,6 @@ // @flow -import t from 'tcomb'; -import type { TList } from 'tcomb'; +import t, { type TInterface, type TList } from 'tcomb'; import { type UpdateActivityResult, @@ -9,8 +8,6 @@ import { type SetThreadUnreadStatusRequest, type SetThreadUnreadStatusResult, type ActivityUpdate, - setThreadUnreadStatusResult, - updateActivityResultValidator, } from 'lib/types/activity-types.js'; import { tShape, tID } from 'lib/utils/validation-utils.js'; @@ -19,7 +16,6 @@ import { activityUpdater, setThreadUnreadStatus, } from '../updaters/activity-updaters.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; const activityUpdatesInputValidator: TList> = t.list( tShape({ @@ -29,44 +25,29 @@ const activityUpdatesInputValidator: TList> = t.list( }), ); -const inputValidator = tShape({ - updates: activityUpdatesInputValidator, -}); +export const updateActivityResponderInputValidator: TInterface = + tShape({ + updates: activityUpdatesInputValidator, + }); async function updateActivityResponder( viewer: Viewer, - input: mixed, + request: UpdateActivityRequest, ): Promise { - const request = await validateInput(viewer, inputValidator, input); - const result = await activityUpdater(viewer, request); - return validateOutput( - viewer.platformDetails, - updateActivityResultValidator, - result, - ); + return await activityUpdater(viewer, request); } -const setThreadUnreadStatusValidator = tShape({ - threadID: tID, - unread: t.Bool, - latestMessage: t.maybe(tID), -}); +export const setThreadUnreadStatusValidator: TInterface = + tShape({ + threadID: tID, + unread: t.Bool, + latestMessage: t.maybe(tID), + }); async function threadSetUnreadStatusResponder( viewer: Viewer, - input: mixed, + request: SetThreadUnreadStatusRequest, ): Promise { - const request = await validateInput( - viewer, - setThreadUnreadStatusValidator, - input, - ); - - const result = await setThreadUnreadStatus(viewer, request); - return validateOutput( - viewer.platformDetails, - setThreadUnreadStatusResult, - result, - ); + return await setThreadUnreadStatus(viewer, request); } export { diff --git a/keyserver/src/responders/device-responders.js b/keyserver/src/responders/device-responders.js index 5c4022b60b..5920541745 100644 --- a/keyserver/src/responders/device-responders.js +++ b/keyserver/src/responders/device-responders.js @@ -8,10 +8,9 @@ import { tShape, tPlatformDetails } from 'lib/utils/validation-utils.js'; import type { Viewer } from '../session/viewer.js'; import { deviceTokenUpdater } from '../updaters/device-token-updaters.js'; -import { validateInput } from '../utils/validation-utils.js'; const deviceTokenUpdateRequestInputValidator: TInterface = - tShape({ + tShape({ deviceToken: t.maybe(t.String), deviceType: t.maybe(t.enums.of(['ios', 'android'])), platformDetails: t.maybe(tPlatformDetails), @@ -19,10 +18,8 @@ const deviceTokenUpdateRequestInputValidator: TInterface { - const request: DeviceTokenUpdateRequest = input; - await validateInput(viewer, deviceTokenUpdateRequestInputValidator, request); await deviceTokenUpdater(viewer, request); } diff --git a/keyserver/src/responders/entry-responders.js b/keyserver/src/responders/entry-responders.js index 270758f737..a625cf37b4 100644 --- a/keyserver/src/responders/entry-responders.js +++ b/keyserver/src/responders/entry-responders.js @@ -46,7 +46,6 @@ import { compareNewCalendarQuery, } from '../updaters/entry-updaters.js'; import { commitSessionUpdate } from '../updaters/session-updaters.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; type EntryQueryInput = { +startDate: string, @@ -55,25 +54,26 @@ type EntryQueryInput = { +includeDeleted?: ?boolean, +filters?: ?$ReadOnlyArray, }; -const entryQueryInputValidator: TInterface = tShape({ - navID: t.maybe(t.String), - startDate: tDate, - endDate: tDate, - includeDeleted: t.maybe(t.Boolean), - filters: t.maybe( - t.list( - t.union([ - tShape({ - type: tString(calendarThreadFilterTypes.NOT_DELETED), - }), - tShape({ - type: tString(calendarThreadFilterTypes.THREAD_LIST), - threadIDs: t.list(tID), - }), - ]), +const entryQueryInputValidator: TInterface = + tShape({ + navID: t.maybe(t.String), + startDate: tDate, + endDate: tDate, + includeDeleted: t.maybe(t.Boolean), + filters: t.maybe( + t.list( + t.union([ + tShape({ + type: tString(calendarThreadFilterTypes.NOT_DELETED), + }), + tShape({ + type: tString(calendarThreadFilterTypes.THREAD_LIST), + threadIDs: t.list(tID), + }), + ]), + ), ), - ), -}); + }); const newEntryQueryInputValidator: TInterface = tShape({ startDate: tDate, @@ -136,29 +136,20 @@ export const fetchEntryInfosResponseValidator: TInterface { - const inputQuery = await validateInput( - viewer, - entryQueryInputValidator, - input, - ); const request = normalizeCalendarQuery(inputQuery); await verifyCalendarQueryThreadIDs(request); const response = await fetchEntryInfos(viewer, [request]); - return validateOutput( - viewer.platformDetails, - fetchEntryInfosResponseValidator, - { - ...response, - userInfos: {}, - }, - ); + return { + ...response, + userInfos: {}, + }; } -const entryRevisionHistoryFetchInputValidator = +export const entryRevisionHistoryFetchInputValidator: TInterface = tShape({ id: tID, }); @@ -170,31 +161,22 @@ export const fetchEntryRevisionInfosResultValidator: TInterface { - const request = await validateInput( - viewer, - entryRevisionHistoryFetchInputValidator, - input, - ); const entryHistory = await fetchEntryRevisionInfo(viewer, request.id); - const response = { result: entryHistory }; - return validateOutput( - viewer.platformDetails, - fetchEntryRevisionInfosResultValidator, - response, - ); + return { result: entryHistory }; } -const createEntryRequestInputValidator = tShape({ - text: t.String, - sessionID: t.maybe(t.String), - timestamp: t.Number, - date: tDate, - threadID: tID, - localID: t.maybe(t.String), - calendarQuery: t.maybe(newEntryQueryInputValidator), -}); +export const createEntryRequestInputValidator: TInterface = + tShape({ + text: t.String, + sessionID: t.maybe(t.String), + timestamp: t.Number, + date: tDate, + threadID: tID, + localID: t.maybe(t.String), + calendarQuery: t.maybe(newEntryQueryInputValidator), + }); export const saveEntryResponseValidator: TInterface = tShape({ @@ -205,54 +187,36 @@ export const saveEntryResponseValidator: TInterface = async function entryCreationResponder( viewer: Viewer, - input: mixed, + request: CreateEntryRequest, ): Promise { - const request = await validateInput( - viewer, - createEntryRequestInputValidator, - input, - ); - const response = await createEntry(viewer, request); - return validateOutput( - viewer.platformDetails, - saveEntryResponseValidator, - response, - ); + return await createEntry(viewer, request); } -const saveEntryRequestInputValidator = tShape({ - entryID: tID, - text: t.String, - prevText: t.String, - sessionID: t.maybe(t.String), - timestamp: t.Number, - calendarQuery: t.maybe(newEntryQueryInputValidator), -}); +export const saveEntryRequestInputValidator: TInterface = + tShape({ + entryID: tID, + text: t.String, + prevText: t.String, + sessionID: t.maybe(t.String), + timestamp: t.Number, + calendarQuery: t.maybe(newEntryQueryInputValidator), + }); async function entryUpdateResponder( viewer: Viewer, - input: mixed, + request: SaveEntryRequest, ): Promise { - const request = await validateInput( - viewer, - saveEntryRequestInputValidator, - input, - ); - const response = await updateEntry(viewer, request); - return validateOutput( - viewer.platformDetails, - saveEntryResponseValidator, - response, - ); + return await updateEntry(viewer, request); } -const deleteEntryRequestInputValidator = tShape({ - entryID: tID, - prevText: t.String, - sessionID: t.maybe(t.String), - timestamp: t.Number, - calendarQuery: t.maybe(newEntryQueryInputValidator), -}); +export const deleteEntryRequestInputValidator: TInterface = + tShape({ + entryID: tID, + prevText: t.String, + sessionID: t.maybe(t.String), + timestamp: t.Number, + calendarQuery: t.maybe(newEntryQueryInputValidator), + }); export const deleteEntryResponseValidator: TInterface = tShape({ @@ -263,27 +227,18 @@ export const deleteEntryResponseValidator: TInterface = async function entryDeletionResponder( viewer: Viewer, - input: mixed, + request: DeleteEntryRequest, ): Promise { - const request = await validateInput( - viewer, - deleteEntryRequestInputValidator, - input, - ); - const response = await deleteEntry(viewer, request); - return validateOutput( - viewer.platformDetails, - deleteEntryResponseValidator, - response, - ); + return await deleteEntry(viewer, request); } -const restoreEntryRequestInputValidator = tShape({ - entryID: tID, - sessionID: t.maybe(t.String), - timestamp: t.Number, - calendarQuery: t.maybe(newEntryQueryInputValidator), -}); +export const restoreEntryRequestInputValidator: TInterface = + tShape({ + entryID: tID, + sessionID: t.maybe(t.String), + timestamp: t.Number, + calendarQuery: t.maybe(newEntryQueryInputValidator), + }); export const restoreEntryResponseValidator: TInterface = tShape({ @@ -293,19 +248,9 @@ export const restoreEntryResponseValidator: TInterface = async function entryRestorationResponder( viewer: Viewer, - input: mixed, + request: RestoreEntryRequest, ): Promise { - const request = await validateInput( - viewer, - restoreEntryRequestInputValidator, - input, - ); - const response = await restoreEntry(viewer, request); - return validateOutput( - viewer.platformDetails, - restoreEntryResponseValidator, - response, - ); + return await restoreEntry(viewer, request); } export const deltaEntryInfosResultValidator: TInterface = @@ -317,14 +262,8 @@ export const deltaEntryInfosResultValidator: TInterface = async function calendarQueryUpdateResponder( viewer: Viewer, - input: mixed, + request: CalendarQuery, ): Promise { - const request = await validateInput( - viewer, - newEntryQueryInputValidator, - input, - ); - await verifyCalendarQueryThreadIDs(request); if (!viewer.loggedIn) { throw new ServerError('not_logged_in'); @@ -338,16 +277,12 @@ async function calendarQueryUpdateResponder( commitSessionUpdate(viewer, sessionUpdate), ]); - return validateOutput( - viewer.platformDetails, - deltaEntryInfosResultValidator, - { - rawEntryInfos: response.rawEntryInfos, - deletedEntryIDs: response.deletedEntryIDs, - // Old clients expect userInfos object - userInfos: [], - }, - ); + return { + rawEntryInfos: response.rawEntryInfos, + deletedEntryIDs: response.deletedEntryIDs, + // Old clients expect userInfos object + userInfos: [], + }; } export { diff --git a/keyserver/src/responders/handlers.js b/keyserver/src/responders/handlers.js index 4b064b7641..2ecc83081b 100644 --- a/keyserver/src/responders/handlers.js +++ b/keyserver/src/responders/handlers.js @@ -1,6 +1,7 @@ // @flow import type { $Response, $Request } from 'express'; +import type { TType } from 'tcomb'; import { ServerError } from 'lib/utils/errors.js'; import { @@ -24,13 +25,35 @@ import { type AppURLFacts, getAppURLFactsFromRequestURL, } from '../utils/urls.js'; -import { policiesValidator } from '../utils/validation-utils.js'; +import { + policiesValidator, + validateInput, + validateOutput, +} from '../utils/validation-utils.js'; -export type JSONResponder = { +type InnerJSONResponder = { responder: (viewer: Viewer, input: any) => Promise<*>, requiredPolicies: $ReadOnlyArray, }; +export opaque type JSONResponder: InnerJSONResponder = InnerJSONResponder; + +function createJSONResponder( + responder: (Viewer, input: I) => Promise, + inputValidator: TType, + outputValidator: TType, + requiredPolicies: $ReadOnlyArray, +): JSONResponder { + return { + responder: async (viewer, input) => { + const request = await validateInput(viewer, inputValidator, input); + const result = await responder(viewer, request); + return validateOutput(viewer.platformDetails, outputValidator, result); + }, + requiredPolicies, + }; +} + export type DownloadResponder = ( viewer: Viewer, req: $Request, @@ -257,6 +280,7 @@ async function handleAsyncPromise(promise: Promise) { } export { + createJSONResponder, jsonHandler, httpGetHandler, downloadHandler, diff --git a/keyserver/src/responders/keys-responders.js b/keyserver/src/responders/keys-responders.js index 254efd98e8..c4fca7794a 100644 --- a/keyserver/src/responders/keys-responders.js +++ b/keyserver/src/responders/keys-responders.js @@ -1,7 +1,7 @@ // @flow import type { Account as OlmAccount } from '@commapp/olm'; -import t, { type TUnion } from 'tcomb'; +import t, { type TUnion, type TInterface } from 'tcomb'; import type { OlmSessionInitializationInfo, @@ -20,16 +20,16 @@ import { verifyClientSupported } from '../session/version.js'; import type { Viewer } from '../session/viewer.js'; import { fetchCallUpdateOlmAccount } from '../updaters/olm-account-updater.js'; import { validateAccountPrekey } from '../utils/olm-utils.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; type AccountKeysSet = { +identityKeys: string, ...OlmSessionInitializationInfo, }; -const getSessionPublicKeysInputValidator = tShape({ - session: t.String, -}); +export const getSessionPublicKeysInputValidator: TInterface = + tShape({ + session: t.String, + }); type GetSessionPublicKeysResponse = SessionPublicKeys | null; export const getSessionPublicKeysResponseValidator: TUnion = @@ -37,22 +37,12 @@ export const getSessionPublicKeysResponseValidator: TUnion { if (!viewer.loggedIn) { return null; } - const request = await validateInput( - viewer, - getSessionPublicKeysInputValidator, - input, - ); - const response = await fetchSessionPublicKeys(request.session); - return validateOutput( - viewer.platformDetails, - getSessionPublicKeysResponseValidator, - response, - ); + return await fetchSessionPublicKeys(request.session); } async function retrieveAccountKeysSet( diff --git a/keyserver/src/responders/link-responders.js b/keyserver/src/responders/link-responders.js index c43f6feafc..e26b5f7a06 100644 --- a/keyserver/src/responders/link-responders.js +++ b/keyserver/src/responders/link-responders.js @@ -9,6 +9,7 @@ import { type InviteLink, inviteLinkValidator, type CreateOrUpdatePublicLinkRequest, + type DisableInviteLinkRequest, } from 'lib/types/link-types.js'; import { tShape, tID } from 'lib/utils/validation-utils.js'; @@ -19,9 +20,8 @@ import { verifyInviteLink, } from '../fetchers/link-fetchers.js'; import { Viewer } from '../session/viewer.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const inviteLinkVerificationRequestInputValidator: TInterface = +export const inviteLinkVerificationRequestInputValidator: TInterface = tShape({ secret: t.String, }); @@ -42,19 +42,9 @@ export const inviteLinkVerificationResponseValidator: TUnion { - const request = await validateInput( - viewer, - inviteLinkVerificationRequestInputValidator, - input, - ); - const response = await verifyInviteLink(viewer, request); - return validateOutput( - viewer.platformDetails, - inviteLinkVerificationResponseValidator, - response, - ); + return await verifyInviteLink(viewer, request); } export const fetchInviteLinksResponseValidator: TInterface = @@ -66,48 +56,34 @@ async function fetchPrimaryInviteLinksResponder( viewer: Viewer, ): Promise { const primaryLinks = await fetchPrimaryInviteLinks(viewer); - return validateOutput( - viewer.platformDetails, - fetchInviteLinksResponseValidator, - { - links: primaryLinks, - }, - ); + return { + links: primaryLinks, + }; } -const createOrUpdatePublicLinkInputValidator: TInterface = - tShape({ +export const createOrUpdatePublicLinkInputValidator: TInterface = + tShape({ name: t.String, communityID: tID, }); async function createOrUpdatePublicLinkResponder( viewer: Viewer, - input: mixed, + request: CreateOrUpdatePublicLinkRequest, ): Promise { - const request = await validateInput( - viewer, - createOrUpdatePublicLinkInputValidator, - input, - ); - const response = await createOrUpdatePublicLink(viewer, request); - return validateOutput(viewer.platformDetails, inviteLinkValidator, response); + return await createOrUpdatePublicLink(viewer, request); } -const disableInviteLinkInputValidator = tShape({ - name: t.String, - communityID: tID, -}); +export const disableInviteLinkInputValidator: TInterface = + tShape({ + name: t.String, + communityID: tID, + }); async function disableInviteLinkResponder( viewer: Viewer, - input: mixed, + request: DisableInviteLinkRequest, ): Promise { - const request = await validateInput( - viewer, - disableInviteLinkInputValidator, - input, - ); await deleteInviteLink(viewer, request); } diff --git a/keyserver/src/responders/message-report-responder.js b/keyserver/src/responders/message-report-responder.js index 11fa8f0cf4..f739c1ff4a 100644 --- a/keyserver/src/responders/message-report-responder.js +++ b/keyserver/src/responders/message-report-responder.js @@ -11,9 +11,8 @@ import { tShape, tID } from 'lib/utils/validation-utils.js'; import createMessageReport from '../creators/message-report-creator.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const messageReportCreationRequestInputValidator = +export const messageReportCreationRequestInputValidator: TInterface = tShape({ messageID: tID, }); @@ -23,21 +22,10 @@ export const messageReportCreationResultValidator: TInterface { - const request = await validateInput( - viewer, - messageReportCreationRequestInputValidator, - input, - ); - const rawMessageInfos = await createMessageReport(viewer, request); - const result = { messageInfo: rawMessageInfos[0] }; - return validateOutput( - viewer.platformDetails, - messageReportCreationResultValidator, - result, - ); + return { messageInfo: rawMessageInfos[0] }; } export { messageReportCreationResponder }; diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js index 81737e414a..00f6c20bd5 100644 --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -1,7 +1,7 @@ // @flow import invariant from 'invariant'; -import t, { type TInterface } from 'tcomb'; +import t, { type TInterface, type TUnion } from 'tcomb'; import { onlyOneEmojiRegex } from 'lib/shared/emojis.js'; import { @@ -63,28 +63,22 @@ import { assignImages, assignMessageContainerToMedia, } from '../updaters/upload-updaters.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const sendTextMessageRequestInputValidator = tShape({ - threadID: tID, - localID: t.maybe(t.String), - text: t.String, - sidebarCreation: t.maybe(t.Boolean), -}); +export const sendTextMessageRequestInputValidator: TInterface = + tShape({ + threadID: tID, + localID: t.maybe(t.String), + text: t.String, + sidebarCreation: t.maybe(t.Boolean), + }); export const sendMessageResponseValidator: TInterface = tShape({ newMessageInfo: rawMessageInfoValidator }); async function textMessageCreationResponder( viewer: Viewer, - input: mixed, + request: SendTextMessageRequest, ): Promise { - const request = await validateInput( - viewer, - sendTextMessageRequestInputValidator, - input, - ); - const { threadID, localID, text: rawText, sidebarCreation } = request; const text = trimMessage(rawText); if (!text) { @@ -124,20 +118,14 @@ async function textMessageCreationResponder( } const rawMessageInfos = await createMessages(viewer, [messageData]); - const response = { newMessageInfo: rawMessageInfos[0] }; - return validateOutput( - viewer.platformDetails, - sendMessageResponseValidator, - response, - ); + return { newMessageInfo: rawMessageInfos[0] }; } -const fetchMessageInfosRequestInputValidator = tShape( - { +export const fetchMessageInfosRequestInputValidator: TInterface = + tShape({ cursors: t.dict(tID, t.maybe(tID)), numberPerThread: t.maybe(t.Number), - }, -); + }); export const fetchMessageInfosResponseValidator: TInterface = tShape({ @@ -148,29 +136,20 @@ export const fetchMessageInfosResponseValidator: TInterface { - const request = await validateInput( - viewer, - fetchMessageInfosRequestInputValidator, - input, - ); const response = await fetchMessageInfos( viewer, { threadCursors: request.cursors }, request.numberPerThread ? request.numberPerThread : defaultNumberPerThread, ); - return validateOutput( - viewer.platformDetails, - fetchMessageInfosResponseValidator, - { - ...response, - userInfos: {}, - }, - ); + return { + ...response, + userInfos: {}, + }; } -const sendMultimediaMessageRequestInputValidator = +export const sendMultimediaMessageRequestInputValidator: TUnion = t.union([ // This option is only used for messageTypes.IMAGES tShape({ @@ -188,14 +167,8 @@ const sendMultimediaMessageRequestInputValidator = ]); async function multimediaMessageCreationResponder( viewer: Viewer, - input: mixed, + request: SendMultimediaMessageRequest, ): Promise { - const request = await validateInput( - viewer, - sendMultimediaMessageRequestInputValidator, - input, - ); - if ( (request.mediaIDs && request.mediaIDs.length === 0) || (request.mediaMessageContents && request.mediaMessageContents.length === 0) @@ -262,15 +235,10 @@ async function multimediaMessageCreationResponder( ); } - const response = { newMessageInfo }; - return validateOutput( - viewer.platformDetails, - sendMessageResponseValidator, - response, - ); + return { newMessageInfo }; } -const sendReactionMessageRequestInputValidator = +export const sendReactionMessageRequestInputValidator: TInterface = tShape({ threadID: tID, localID: t.maybe(t.String), @@ -280,14 +248,8 @@ const sendReactionMessageRequestInputValidator = }); async function reactionMessageCreationResponder( viewer: Viewer, - input: mixed, + request: SendReactionMessageRequest, ): Promise { - const request = await validateInput( - viewer, - sendReactionMessageRequestInputValidator, - input, - ); - const { threadID, localID, targetMessageID, reaction, action } = request; if (!targetMessageID || !reaction) { @@ -345,18 +307,14 @@ async function reactionMessageCreationResponder( const rawMessageInfos = await createMessages(viewer, [messageData]); - const response = { newMessageInfo: rawMessageInfos[0] }; - return validateOutput( - viewer.platformDetails, - sendMessageResponseValidator, - response, - ); + return { newMessageInfo: rawMessageInfos[0] }; } -const editMessageRequestInputValidator = tShape({ - targetMessageID: tID, - text: t.String, -}); +export const editMessageRequestInputValidator: TInterface = + tShape({ + targetMessageID: tID, + text: t.String, + }); export const sendEditMessageResponseValidator: TInterface = tShape({ @@ -365,14 +323,8 @@ export const sendEditMessageResponseValidator: TInterface { - const request = await validateInput( - viewer, - editMessageRequestInputValidator, - input, - ); - const { targetMessageID, text: rawText } = request; const text = trimMessage(rawText); if (!targetMessageID || !text) { @@ -444,15 +396,10 @@ async function editMessageCreationResponder( const newMessageInfos = await createMessages(viewer, messagesData); - const response = { newMessageInfos }; - return validateOutput( - viewer.platformDetails, - sendEditMessageResponseValidator, - response, - ); + return { newMessageInfos }; } -const fetchPinnedMessagesResponderInputValidator = +export const fetchPinnedMessagesResponderInputValidator: TInterface = tShape({ threadID: tID, }); @@ -464,28 +411,19 @@ export const fetchPinnedMessagesResultValidator: TInterface { - const request = await validateInput( - viewer, - fetchPinnedMessagesResponderInputValidator, - input, - ); - const response = await fetchPinnedMessageInfos(viewer, request); - return validateOutput( - viewer.platformDetails, - fetchPinnedMessagesResultValidator, - response, - ); + return await fetchPinnedMessageInfos(viewer, request); } -const searchMessagesResponderInputValidator = tShape({ - query: t.String, - threadID: tID, - cursor: t.maybe(tID), -}); +export const searchMessagesResponderInputValidator: TInterface = + tShape({ + query: t.String, + threadID: tID, + cursor: t.maybe(tID), + }); -const searchMessagesResponseValidator: TInterface = +export const searchMessagesResponseValidator: TInterface = tShape({ messages: t.list(rawMessageInfoValidator), endReached: t.Boolean, @@ -493,25 +431,14 @@ const searchMessagesResponseValidator: TInterface = async function searchMessagesResponder( viewer: Viewer, - input: mixed, + request: SearchMessagesRequest, ): Promise { - const request: SearchMessagesRequest = await validateInput( - viewer, - searchMessagesResponderInputValidator, - input, - ); - - const response = await searchMessagesInSingleChat( + return await searchMessagesInSingleChat( request.query, request.threadID, viewer, request.cursor, ); - return validateOutput( - viewer.platformDetails, - searchMessagesResponseValidator, - response, - ); } export { diff --git a/keyserver/src/responders/relationship-responders.js b/keyserver/src/responders/relationship-responders.js index 87a48c16d4..da8bb90674 100644 --- a/keyserver/src/responders/relationship-responders.js +++ b/keyserver/src/responders/relationship-responders.js @@ -11,12 +11,12 @@ import { tShape } from 'lib/utils/validation-utils.js'; import type { Viewer } from '../session/viewer.js'; import { updateRelationships } from '../updaters/relationship-updaters.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const updateRelationshipInputValidator = tShape({ - action: t.enums.of(relationshipActionsList, 'relationship action'), - userIDs: t.list(t.String), -}); +export const updateRelationshipInputValidator: TInterface = + tShape({ + action: t.enums.of(relationshipActionsList, 'relationship action'), + userIDs: t.list(t.String), + }); export const relationshipErrorsValidator: TInterface = tShape({ @@ -27,19 +27,9 @@ export const relationshipErrorsValidator: TInterface = async function updateRelationshipsResponder( viewer: Viewer, - input: mixed, + request: RelationshipRequest, ): Promise { - const request = await validateInput( - viewer, - updateRelationshipInputValidator, - input, - ); - const response = await updateRelationships(viewer, request); - return validateOutput( - viewer.platformDetails, - relationshipErrorsValidator, - response, - ); + return await updateRelationships(viewer, request); } export { updateRelationshipsResponder }; diff --git a/keyserver/src/responders/report-responders.js b/keyserver/src/responders/report-responders.js index e67cb739ed..5614a58ad3 100644 --- a/keyserver/src/responders/report-responders.js +++ b/keyserver/src/responders/report-responders.js @@ -2,7 +2,7 @@ import type { $Response, $Request } from 'express'; import t from 'tcomb'; -import type { TInterface, TStructProps } from 'tcomb'; +import type { TInterface, TStructProps, TUnion } from 'tcomb'; import { type ReportCreationResponse, @@ -29,7 +29,6 @@ import { fetchReduxToolsImport, } from '../fetchers/report-fetchers.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; const tActionSummary = tShape({ type: t.String, @@ -107,44 +106,40 @@ const userInconsistencyReportCreationRequest = tShape({ ), }); -const reportCreationRequestInputValidator = t.union([ - tShape({ - type: t.maybe( - t.irreducible('reportTypes.ERROR', x => x === reportTypes.ERROR), - ), - platformDetails: t.maybe(tPlatformDetails), - deviceType: t.maybe(tPlatform), - codeVersion: t.maybe(t.Number), - stateVersion: t.maybe(t.Number), - errors: t.list( - tShape({ - errorMessage: t.String, - stack: t.maybe(t.String), - componentStack: t.maybe(t.String), - }), - ), - preloadedState: t.Object, - currentState: t.Object, - actions: t.list(t.union([t.Object, t.String])), - }), - threadInconsistencyReportCreationRequest, - entryInconsistencyReportCreationRquest, - mediaMissionReportCreationRequest, - userInconsistencyReportCreationRequest, -]); +export const reportCreationRequestInputValidator: TUnion = + t.union([ + tShape({ + type: t.maybe( + t.irreducible('reportTypes.ERROR', x => x === reportTypes.ERROR), + ), + platformDetails: t.maybe(tPlatformDetails), + deviceType: t.maybe(tPlatform), + codeVersion: t.maybe(t.Number), + stateVersion: t.maybe(t.Number), + errors: t.list( + tShape({ + errorMessage: t.String, + stack: t.maybe(t.String), + componentStack: t.maybe(t.String), + }), + ), + preloadedState: t.Object, + currentState: t.Object, + actions: t.list(t.union([t.Object, t.String])), + }), + threadInconsistencyReportCreationRequest, + entryInconsistencyReportCreationRquest, + mediaMissionReportCreationRequest, + userInconsistencyReportCreationRequest, + ]); export const reportCreationResponseValidator: TInterface = tShape({ id: t.String }); async function reportCreationResponder( viewer: Viewer, - input: mixed, + request: ReportCreationRequest, ): Promise { - let request = await validateInput( - viewer, - reportCreationRequestInputValidator, - input, - ); if (request.type === null || request.type === undefined) { request.type = reportTypes.ERROR; } @@ -159,14 +154,10 @@ async function reportCreationResponder( if (!response) { throw new ServerError('ignored_report'); } - return validateOutput( - viewer.platformDetails, - reportCreationResponseValidator, - response, - ); + return response; } -const reportMultiCreationRequestInputValidator = +export const reportMultiCreationRequestInputValidator: TInterface = tShape({ reports: t.list( t.union([ @@ -200,13 +191,8 @@ type ReportMultiCreationRequest = { }; async function reportMultiCreationResponder( viewer: Viewer, - input: mixed, + request: ReportMultiCreationRequest, ): Promise { - const request = await validateInput( - viewer, - reportMultiCreationRequestInputValidator, - input, - ); await Promise.all( request.reports.map(reportCreationRequest => createReport(viewer, reportCreationRequest), @@ -214,7 +200,7 @@ async function reportMultiCreationResponder( ); } -const fetchErrorReportInfosRequestInputValidator = +export const fetchErrorReportInfosRequestInputValidator: TInterface = tShape({ cursor: t.maybe(t.String), }); @@ -227,19 +213,9 @@ export const fetchErrorReportInfosResponseValidator: TInterface { - const request = await validateInput( - viewer, - fetchErrorReportInfosRequestInputValidator, - input, - ); - const response = await fetchErrorReportInfos(viewer, request); - return validateOutput( - viewer.platformDetails, - fetchErrorReportInfosResponseValidator, - response, - ); + return await fetchErrorReportInfos(viewer, request); } async function errorReportDownloadResponder( diff --git a/keyserver/src/responders/responder-validators.test.js b/keyserver/src/responders/responder-validators.test.js index f16915a83c..e0f8dd93dd 100644 --- a/keyserver/src/responders/responder-validators.test.js +++ b/keyserver/src/responders/responder-validators.test.js @@ -1,7 +1,7 @@ // @flow import { - setThreadUnreadStatusResult, + setThreadUnreadStatusResultValidator, updateActivityResultValidator, } from 'lib/types/activity-types.js'; @@ -430,10 +430,10 @@ describe('activity responder', () => { it('should validate set thread unread response', () => { const response = { resetToUnread: false }; - expect(setThreadUnreadStatusResult.is(response)).toBe(true); - expect(setThreadUnreadStatusResult.is({ ...response, unread: false })).toBe( - false, - ); + expect(setThreadUnreadStatusResultValidator.is(response)).toBe(true); + expect( + setThreadUnreadStatusResultValidator.is({ ...response, unread: false }), + ).toBe(false); }); }); diff --git a/keyserver/src/responders/search-responders.js b/keyserver/src/responders/search-responders.js index 2addade89d..dd6724d798 100644 --- a/keyserver/src/responders/search-responders.js +++ b/keyserver/src/responders/search-responders.js @@ -13,11 +13,11 @@ import { tShape } from 'lib/utils/validation-utils.js'; import { searchForUsers, searchForUser } from '../search/users.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const userSearchRequestInputValidator = tShape({ - prefix: t.maybe(t.String), -}); +export const userSearchRequestInputValidator: TInterface = + tShape({ + prefix: t.maybe(t.String), + }); export const userSearchResultValidator: TInterface = tShape({ @@ -26,46 +26,28 @@ export const userSearchResultValidator: TInterface = async function userSearchResponder( viewer: Viewer, - input: mixed, + request: UserSearchRequest, ): Promise { - const request = await validateInput( - viewer, - userSearchRequestInputValidator, - input, - ); const searchResults = await searchForUsers(request); - const result = { userInfos: searchResults }; - return validateOutput( - viewer.platformDetails, - userSearchResultValidator, - result, - ); + return { userInfos: searchResults }; } -const exactUserSearchRequestInputValidator = tShape({ - username: t.String, -}); +export const exactUserSearchRequestInputValidator: TInterface = + tShape({ + username: t.String, + }); -const exactUserSearchResultValidator = tShape({ - userInfo: t.maybe(globalAccountUserInfoValidator), -}); +export const exactUserSearchResultValidator: TInterface = + tShape({ + userInfo: t.maybe(globalAccountUserInfoValidator), + }); async function exactUserSearchResponder( viewer: Viewer, - input: mixed, + request: ExactUserSearchRequest, ): Promise { - const request = await validateInput( - viewer, - exactUserSearchRequestInputValidator, - input, - ); const searchResult = await searchForUser(request.username); - const result = { userInfo: searchResult }; - return validateOutput( - viewer.platformDetails, - exactUserSearchResultValidator, - result, - ); + return { userInfo: searchResult }; } export { userSearchResponder, exactUserSearchResponder }; diff --git a/keyserver/src/responders/siwe-nonce-responders.js b/keyserver/src/responders/siwe-nonce-responders.js index ef2b692c60..20aac7401d 100644 --- a/keyserver/src/responders/siwe-nonce-responders.js +++ b/keyserver/src/responders/siwe-nonce-responders.js @@ -7,18 +7,16 @@ import type { SIWENonceResponse } from 'lib/types/siwe-types.js'; import { tShape } from 'lib/utils/validation-utils.js'; import { createSIWENonceEntry } from '../creators/siwe-nonce-creator.js'; -import type { Viewer } from '../session/viewer.js'; -import { validateOutput } from '../utils/validation-utils.js'; export const siweNonceResponseValidator: TInterface = tShape({ nonce: t.String }); -async function siweNonceResponder(viewer: Viewer): Promise { +async function siweNonceResponder(): Promise { const generatedNonce = generateNonce(); await createSIWENonceEntry(generatedNonce); - return validateOutput(viewer.platformDetails, siweNonceResponseValidator, { + return { nonce: generatedNonce, - }); + }; } export { siweNonceResponder }; diff --git a/keyserver/src/responders/thread-responders.js b/keyserver/src/responders/thread-responders.js index ae4f6a55f6..09b02798ab 100644 --- a/keyserver/src/responders/thread-responders.js +++ b/keyserver/src/responders/thread-responders.js @@ -62,12 +62,12 @@ import { joinThread, toggleMessagePinForThread, } from '../updaters/thread-updaters.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; -const threadDeletionRequestInputValidator = tShape({ - threadID: tID, - accountPassword: t.maybe(tPassword), -}); +export const threadDeletionRequestInputValidator: TInterface = + tShape({ + threadID: tID, + accountPassword: t.maybe(tPassword), + }); export const leaveThreadResultValidator: TInterface = tShape({ @@ -78,19 +78,9 @@ export const leaveThreadResultValidator: TInterface = async function threadDeletionResponder( viewer: Viewer, - input: mixed, + request: ThreadDeletionRequest, ): Promise { - const request = await validateInput( - viewer, - threadDeletionRequestInputValidator, - input, - ); - const result = await deleteThread(viewer, request); - return validateOutput( - viewer.platformDetails, - leaveThreadResultValidator, - result, - ); + return await deleteThread(viewer, request); } export const roleChangeRequestInputValidator: TInterface = @@ -116,93 +106,56 @@ export const changeThreadSettingsResultValidator: TInterface { - const request = await validateInput( - viewer, - roleChangeRequestInputValidator, - input, - ); - const result = await updateRole(viewer, request); - return validateOutput( - viewer.platformDetails, - changeThreadSettingsResultValidator, - result, - ); + return await updateRole(viewer, request); } -const removeMembersRequestInputValidator = tShape({ - threadID: tID, - memberIDs: t.list(t.String), -}); +export const removeMembersRequestInputValidator: TInterface = + tShape({ + threadID: tID, + memberIDs: t.list(t.String), + }); async function memberRemovalResponder( viewer: Viewer, - input: mixed, + request: RemoveMembersRequest, ): Promise { - const request = await validateInput( - viewer, - removeMembersRequestInputValidator, - input, - ); - const result = await removeMembers(viewer, request); - return validateOutput( - viewer.platformDetails, - changeThreadSettingsResultValidator, - result, - ); + return await removeMembers(viewer, request); } -const leaveThreadRequestInputValidator = tShape({ - threadID: tID, -}); +export const leaveThreadRequestInputValidator: TInterface = + tShape({ + threadID: tID, + }); async function threadLeaveResponder( viewer: Viewer, - input: mixed, + request: LeaveThreadRequest, ): Promise { - const request = await validateInput( - viewer, - leaveThreadRequestInputValidator, - input, - ); - const result = await leaveThread(viewer, request); - return validateOutput( - viewer.platformDetails, - leaveThreadResultValidator, - result, - ); + return await leaveThread(viewer, request); } -const updateThreadRequestInputValidator = tShape({ - threadID: tID, - changes: tShape({ - type: t.maybe(tNumEnum(values(threadTypes))), - name: t.maybe(t.String), - description: t.maybe(t.String), - color: t.maybe(tColor), - parentThreadID: t.maybe(tID), - newMemberIDs: t.maybe(t.list(t.String)), - avatar: t.maybe(updateUserAvatarRequestValidator), - }), - accountPassword: t.maybe(tPassword), -}); +export const updateThreadRequestInputValidator: TInterface = + tShape({ + threadID: tID, + changes: tShape({ + type: t.maybe(tNumEnum(values(threadTypes))), + name: t.maybe(t.String), + description: t.maybe(t.String), + color: t.maybe(tColor), + parentThreadID: t.maybe(tID), + newMemberIDs: t.maybe(t.list(t.String)), + avatar: t.maybe(updateUserAvatarRequestValidator), + }), + accountPassword: t.maybe(tPassword), + }); async function threadUpdateResponder( viewer: Viewer, - input: mixed, + request: UpdateThreadRequest, ): Promise { - const request = await validateInput( - viewer, - updateThreadRequestInputValidator, - input, - ); - const result = await updateThread(viewer, request); - return validateOutput( - viewer.platformDetails, - changeThreadSettingsResultValidator, - result, - ); + return await updateThread(viewer, request); } const threadRequestValidationShape = { @@ -244,29 +197,19 @@ export const newThreadResponseValidator: TInterface = async function threadCreationResponder( viewer: Viewer, - input: mixed, + request: ServerNewThreadRequest, ): Promise { - const request = await validateInput( - viewer, - newThreadRequestInputValidator, - input, - ); - - const result = await createThread(viewer, request, { + return await createThread(viewer, request, { silentlyFailMembers: request.type === threadTypes.SIDEBAR, }); - return validateOutput( - viewer.platformDetails, - newThreadResponseValidator, - result, - ); } -const joinThreadRequestInputValidator = tShape({ - threadID: tID, - calendarQuery: t.maybe(entryQueryInputValidator), - inviteLinkSecret: t.maybe(t.String), -}); +export const joinThreadRequestInputValidator: TInterface = + tShape({ + threadID: tID, + calendarQuery: t.maybe(entryQueryInputValidator), + inviteLinkSecret: t.maybe(t.String), + }); export const threadJoinResultValidator: TInterface = tShape({ @@ -280,56 +223,37 @@ export const threadJoinResultValidator: TInterface = async function threadJoinResponder( viewer: Viewer, - input: mixed, + request: ServerThreadJoinRequest, ): Promise { - const request = await validateInput( - viewer, - joinThreadRequestInputValidator, - input, - ); - if (request.calendarQuery) { await verifyCalendarQueryThreadIDs(request.calendarQuery); } - const result = await joinThread(viewer, request); - return validateOutput( - viewer.platformDetails, - threadJoinResultValidator, - result, - ); + return await joinThread(viewer, request); } -const threadFetchMediaRequestInputValidator = tShape({ - threadID: tID, - limit: t.Number, - offset: t.Number, -}); +export const threadFetchMediaRequestInputValidator: TInterface = + tShape({ + threadID: tID, + limit: t.Number, + offset: t.Number, + }); export const threadFetchMediaResultValidator: TInterface = tShape({ media: t.list(mediaValidator) }); async function threadFetchMediaResponder( viewer: Viewer, - input: mixed, + request: ThreadFetchMediaRequest, ): Promise { - const request = await validateInput( - viewer, - threadFetchMediaRequestInputValidator, - input, - ); - const result = await fetchMediaForThread(viewer, request); - return validateOutput( - viewer.platformDetails, - threadFetchMediaResultValidator, - result, - ); + return await fetchMediaForThread(viewer, request); } -const toggleMessagePinRequestInputValidator = tShape({ - messageID: tID, - action: t.enums.of(['pin', 'unpin']), -}); +export const toggleMessagePinRequestInputValidator: TInterface = + tShape({ + messageID: tID, + action: t.enums.of(['pin', 'unpin']), + }); export const toggleMessagePinResultValidator: TInterface = tShape({ @@ -339,22 +263,12 @@ export const toggleMessagePinResultValidator: TInterface async function toggleMessagePinResponder( viewer: Viewer, - input: mixed, + request: ToggleMessagePinRequest, ): Promise { - const request = await validateInput( - viewer, - toggleMessagePinRequestInputValidator, - input, - ); - const result = await toggleMessagePinForThread(viewer, request); - return validateOutput( - viewer.platformDetails, - toggleMessagePinResultValidator, - result, - ); + return await toggleMessagePinForThread(viewer, request); } -const roleModificationRequestInputValidator: TUnion = +export const roleModificationRequestInputValidator: TUnion = t.union([ tShape({ community: tID, @@ -381,25 +295,16 @@ export const roleModificationResultValidator: TInterface async function roleModificationResponder( viewer: Viewer, - input: mixed, + request: RoleModificationRequest, ): Promise { - const request = await validateInput( - viewer, - roleModificationRequestInputValidator, - input, - ); - const response = await modifyRole(viewer, request); - return validateOutput( - viewer.platformDetails, - roleModificationResultValidator, - response, - ); + return await modifyRole(viewer, request); } -const roleDeletionRequestInputValidator = tShape({ - community: tID, - roleID: tID, -}); +export const roleDeletionRequestInputValidator: TInterface = + tShape({ + community: tID, + roleID: tID, + }); export const roleDeletionResultValidator: TInterface = tShape({ @@ -411,19 +316,9 @@ export const roleDeletionResultValidator: TInterface = async function roleDeletionResponder( viewer: Viewer, - input: mixed, + request: RoleDeletionRequest, ): Promise { - const request = await validateInput( - viewer, - roleDeletionRequestInputValidator, - input, - ); - const response = await deleteRole(viewer, request); - return validateOutput( - viewer.platformDetails, - roleDeletionResultValidator, - response, - ); + return await deleteRole(viewer, request); } export { diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js index 4d6281ed0e..4cc52f5958 100644 --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -3,7 +3,7 @@ import type { Utility as OlmUtility } from '@commapp/olm'; import invariant from 'invariant'; import { ErrorTypes, SiweMessage } from 'siwe'; -import t, { type TInterface } from 'tcomb'; +import t, { type TInterface, type TUnion } from 'tcomb'; import bcrypt from 'twin-bcrypt'; import { @@ -33,6 +33,7 @@ import { type ClientAvatar, clientAvatarValidator, type UpdateUserAvatarResponse, + type UpdateUserAvatarRequest, } from 'lib/types/avatar-types.js'; import type { IdentityKeysBlob, @@ -66,7 +67,6 @@ import { oldLoggedInUserInfoValidator, userInfoValidator, } from 'lib/types/user-types.js'; -import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js'; import { identityKeysBlobValidator, signedIdentityKeysBlobValidator, @@ -132,9 +132,8 @@ import { 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, validateOutput } from '../utils/validation-utils.js'; -const subscriptionUpdateRequestInputValidator = +export const subscriptionUpdateRequestInputValidator: TInterface = tShape({ threadID: tID, updatedFields: tShape({ @@ -150,40 +149,27 @@ export const subscriptionUpdateResponseValidator: TInterface { - const request = await validateInput( - viewer, - subscriptionUpdateRequestInputValidator, - input, - ); const threadSubscription = await userSubscriptionUpdater(viewer, request); - return validateOutput( - viewer.platformDetails, - subscriptionUpdateResponseValidator, - { - threadSubscription, - }, - ); + return { + threadSubscription, + }; } -const accountUpdateInputValidator = tShape({ - updatedFields: tShape({ - email: t.maybe(tEmail), - password: t.maybe(tPassword), - }), - currentPassword: tPassword, -}); +export const accountUpdateInputValidator: TInterface = + tShape({ + updatedFields: tShape({ + email: t.maybe(tEmail), + password: t.maybe(tPassword), + }), + currentPassword: tPassword, + }); async function passwordUpdateResponder( viewer: Viewer, - input: mixed, + request: PasswordUpdate, ): Promise { - const request = await validateInput( - viewer, - accountUpdateInputValidator, - input, - ); await accountUpdater(viewer, request); } @@ -194,19 +180,15 @@ async function sendVerificationEmailResponder(viewer: Viewer): Promise { await checkAndSendVerificationEmail(viewer); } -const resetPasswordRequestInputValidator = tShape({ - usernameOrEmail: t.union([tEmail, tOldValidUsername]), -}); +export const resetPasswordRequestInputValidator: TInterface = + tShape({ + usernameOrEmail: t.union([tEmail, tOldValidUsername]), + }); async function sendPasswordResetEmailResponder( viewer: Viewer, - input: mixed, + request: ResetPasswordRequest, ): Promise { - const request = await validateInput( - viewer, - resetPasswordRequestInputValidator, - input, - ); await checkAndSendPasswordResetEmail(request); } @@ -229,39 +211,26 @@ async function logOutResponder(viewer: Viewer): Promise { ]); viewer.setNewCookie(anonymousViewerData); } - const response = { + return { currentUserInfo: { id: viewer.id, anonymous: true, }, }; - return validateOutput( - viewer.platformDetails, - logOutResponseValidator, - response, - ); } -const deleteAccountRequestInputValidator = tShape({ - password: t.maybe(tPassword), -}); +export const deleteAccountRequestInputValidator: TInterface = + tShape({ + password: t.maybe(tPassword), + }); async function accountDeletionResponder( viewer: Viewer, - input: mixed, + request: DeleteAccountRequest, ): Promise { - const request = await validateInput( - viewer, - deleteAccountRequestInputValidator, - input, - ); const result = await deleteAccount(viewer, request); invariant(result, 'deleteAccount should return result if handed request'); - return validateOutput( - viewer.platformDetails, - logOutResponseValidator, - result, - ); + return result; } const deviceTokenUpdateRequestInputValidator = tShape({ @@ -269,19 +238,20 @@ const deviceTokenUpdateRequestInputValidator = tShape({ deviceToken: t.String, }); -const registerRequestInputValidator = tShape({ - username: t.String, - email: t.maybe(tEmail), - password: tPassword, - calendarQuery: t.maybe(newEntryQueryInputValidator), - deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), - platformDetails: tPlatformDetails, - // We include `primaryIdentityPublicKey` to avoid breaking - // old clients, but we no longer do anything with it. - primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), - signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), - initialNotificationsEncryptedMessage: t.maybe(t.String), -}); +export const registerRequestInputValidator: TInterface = + tShape({ + username: t.String, + email: t.maybe(tEmail), + password: tPassword, + calendarQuery: t.maybe(newEntryQueryInputValidator), + deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), + platformDetails: tPlatformDetails, + // We include `primaryIdentityPublicKey` to avoid breaking + // old clients, but we no longer do anything with it. + primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), + signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), + initialNotificationsEncryptedMessage: t.maybe(t.String), + }); export const registerResponseValidator: TInterface = tShape({ @@ -299,13 +269,8 @@ export const registerResponseValidator: TInterface = async function accountCreationResponder( viewer: Viewer, - input: mixed, + request: RegisterRequest, ): Promise { - const request = await validateInput( - viewer, - registerRequestInputValidator, - input, - ); const { signedIdentityKeysBlob } = request; if (signedIdentityKeysBlob) { const identityKeys: IdentityKeysBlob = JSON.parse( @@ -326,12 +291,7 @@ async function accountCreationResponder( throw new ServerError('invalid_signature'); } } - const response = await createAccount(viewer, request); - return validateOutput( - viewer.platformDetails, - registerResponseValidator, - response, - ); + return await createAccount(viewer, request); } type ProcessSuccessfulLoginParams = { @@ -453,21 +413,22 @@ async function processSuccessfulLogin( return response; } -const logInRequestInputValidator = tShape({ - username: t.maybe(t.String), - usernameOrEmail: t.maybe(t.union([tEmail, tOldValidUsername])), - password: tPassword, - watchedIDs: t.list(tID), - calendarQuery: t.maybe(entryQueryInputValidator), - deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), - platformDetails: tPlatformDetails, - source: t.maybe(t.enums.of(values(logInActionSources))), - // We include `primaryIdentityPublicKey` to avoid breaking - // old clients, but we no longer do anything with it. - primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), - signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), - initialNotificationsEncryptedMessage: t.maybe(t.String), -}); +export const logInRequestInputValidator: TInterface = + tShape({ + username: t.maybe(t.String), + usernameOrEmail: t.maybe(t.union([tEmail, tOldValidUsername])), + password: tPassword, + watchedIDs: t.list(tID), + calendarQuery: t.maybe(entryQueryInputValidator), + deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), + platformDetails: tPlatformDetails, + source: t.maybe(t.enums.of(values(logInActionSources))), + // We include `primaryIdentityPublicKey` to avoid breaking + // old clients, but we no longer do anything with it. + primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), + signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), + initialNotificationsEncryptedMessage: t.maybe(t.String), + }); export const logInResponseValidator: TInterface = tShape({ @@ -489,14 +450,8 @@ export const logInResponseValidator: TInterface = async function logInResponder( viewer: Viewer, - input: mixed, + request: LogInRequest, ): Promise { - const request = await validateInput( - viewer, - logInRequestInputValidator, - input, - ); - let identityKeys: ?IdentityKeysBlob; const { signedIdentityKeysBlob, initialNotificationsEncryptedMessage } = request; @@ -557,42 +512,32 @@ async function logInResponder( const id = userRow.id.toString(); - const response = await processSuccessfulLogin({ + return await processSuccessfulLogin({ viewer, - input, + input: request, userID: id, calendarQuery, signedIdentityKeysBlob, initialNotificationsEncryptedMessage, }); - return validateOutput( - viewer.platformDetails, - logInResponseValidator, - response, - ); } -const siweAuthRequestInputValidator = tShape({ - signature: t.String, - message: t.String, - calendarQuery: entryQueryInputValidator, - deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), - platformDetails: tPlatformDetails, - watchedIDs: t.list(tID), - signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), - initialNotificationsEncryptedMessage: t.maybe(t.String), -}); +export const siweAuthRequestInputValidator: TInterface = + tShape({ + signature: t.String, + message: t.String, + calendarQuery: entryQueryInputValidator, + deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), + platformDetails: tPlatformDetails, + watchedIDs: t.list(tID), + signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), + initialNotificationsEncryptedMessage: t.maybe(t.String), + }); async function siweAuthResponder( viewer: Viewer, - input: mixed, + request: SIWEAuthRequest, ): Promise { - const request = await validateInput( - viewer, - siweAuthRequestInputValidator, - input, - ); - const { message, signature, @@ -703,86 +648,62 @@ async function siweAuthResponder( } // 9. Complete login with call to `processSuccessfulLogin(...)`. - const response = await processSuccessfulLogin({ + return await processSuccessfulLogin({ viewer, - input, + input: request, userID, calendarQuery, socialProof, signedIdentityKeysBlob, initialNotificationsEncryptedMessage, }); - return validateOutput( - viewer.platformDetails, - logInResponseValidator, - response, - ); } -const updatePasswordRequestInputValidator = tShape({ - code: t.String, - password: tPassword, - watchedIDs: t.list(tID), - calendarQuery: t.maybe(entryQueryInputValidator), - deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), - platformDetails: tPlatformDetails, -}); +export const updatePasswordRequestInputValidator: TInterface = + tShape({ + code: t.String, + password: tPassword, + watchedIDs: t.list(tID), + calendarQuery: t.maybe(entryQueryInputValidator), + deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), + platformDetails: tPlatformDetails, + }); async function oldPasswordUpdateResponder( viewer: Viewer, - input: mixed, + request: UpdatePasswordRequest, ): Promise { - const request = await validateInput( - viewer, - updatePasswordRequestInputValidator, - input, - ); - if (request.calendarQuery) { request.calendarQuery = normalizeCalendarQuery(request.calendarQuery); } - const response = await updatePassword(viewer, request); - return validateOutput( - viewer.platformDetails, - logInResponseValidator, - response, - ); + return await updatePassword(viewer, request); } -const updateUserSettingsInputValidator = tShape({ - name: t.irreducible( - userSettingsTypes.DEFAULT_NOTIFICATIONS, - x => x === userSettingsTypes.DEFAULT_NOTIFICATIONS, - ), - data: t.enums.of(notificationTypeValues), -}); +export const updateUserSettingsInputValidator: TInterface = + tShape({ + name: t.irreducible( + userSettingsTypes.DEFAULT_NOTIFICATIONS, + x => x === userSettingsTypes.DEFAULT_NOTIFICATIONS, + ), + data: t.enums.of(notificationTypeValues), + }); async function updateUserSettingsResponder( viewer: Viewer, - input: mixed, + request: UpdateUserSettingsRequest, ): Promise { - const request = await validateInput( - viewer, - updateUserSettingsInputValidator, - input, - ); await updateUserSettings(viewer, request); } -const policyAcknowledgmentRequestInputValidator = +export const policyAcknowledgmentRequestInputValidator: TInterface = tShape({ policy: t.maybe(t.enums.of(policies)), }); async function policyAcknowledgmentResponder( viewer: Viewer, - input: mixed, + request: PolicyAcknowledgmentRequest, ): Promise { - const request = await validateInput( - viewer, - policyAcknowledgmentRequestInputValidator, - input, - ); await viewerAcknowledgmentUpdater(viewer, request.policy); } @@ -791,26 +712,18 @@ export const updateUserAvatarResponseValidator: TInterface = t.union([ t.maybe(clientAvatarValidator), updateUserAvatarResponseValidator, ]); async function updateUserAvatarResponder( viewer: Viewer, - input: mixed, + request: UpdateUserAvatarRequest, ): Promise { - const request = await validateInput( - viewer, - updateUserAvatarRequestValidator, - input, - ); - const result = await updateUserAvatar(viewer, request); - return validateOutput( - viewer.platformDetails, - updateUserAvatarResponderValidator, - result, - ); + return await updateUserAvatar(viewer, request); } export { diff --git a/keyserver/src/responders/verification-responders.js b/keyserver/src/responders/verification-responders.js index 7f5813ec51..ba42f85232 100644 --- a/keyserver/src/responders/verification-responders.js +++ b/keyserver/src/responders/verification-responders.js @@ -1,25 +1,26 @@ // @flow import t from 'tcomb'; +import type { TInterface } from 'tcomb'; import type { HandleVerificationCodeResult } from 'lib/types/verify-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { tShape } from 'lib/utils/validation-utils.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput } from '../utils/validation-utils.js'; -const codeVerificationRequestInputValidator = tShape({ - code: t.String, -}); +export type CodeVerificationRequest = { code: string }; + +export const codeVerificationRequestInputValidator: TInterface = + tShape({ + code: t.String, + }); /* eslint-disable no-unused-vars */ async function codeVerificationResponder( viewer: Viewer, - input: any, + request: CodeVerificationRequest, ): Promise { - /* eslint-enable no-unused-vars */ - await validateInput(viewer, codeVerificationRequestInputValidator, input); // We have no way to handle this request anymore throw new ServerError('deprecated'); } diff --git a/keyserver/src/responders/version-responders.js b/keyserver/src/responders/version-responders.js index 6ab06ef764..e6e9554b60 100644 --- a/keyserver/src/responders/version-responders.js +++ b/keyserver/src/responders/version-responders.js @@ -6,20 +6,13 @@ import { webAndKeyserverCodeVersion } from 'lib/facts/version.js'; import type { VersionResponse } from 'lib/types/device-types.js'; import { tShape } from 'lib/utils/validation-utils.js'; -import type { Viewer } from '../session/viewer.js'; -import { validateOutput } from '../utils/validation-utils.js'; - -const versionResponseValidator: TInterface = +export const versionResponseValidator: TInterface = tShape({ codeVersion: t.Number }); const versionResponse = { codeVersion: webAndKeyserverCodeVersion }; -async function versionResponder(viewer: Viewer): Promise { - return validateOutput( - viewer.platformDetails, - versionResponseValidator, - versionResponse, - ); +async function versionResponder(): Promise { + return versionResponse; } export { versionResponder }; diff --git a/keyserver/src/uploads/uploads.js b/keyserver/src/uploads/uploads.js index 83b83887ed..1fe59e8dde 100644 --- a/keyserver/src/uploads/uploads.js +++ b/keyserver/src/uploads/uploads.js @@ -27,7 +27,7 @@ import { } from '../fetchers/upload-fetchers.js'; import type { MulterRequest } from '../responders/handlers.js'; import type { Viewer } from '../session/viewer.js'; -import { validateInput, validateOutput } from '../utils/validation-utils.js'; +import { validateOutput } from '../utils/validation-utils.js'; const upload = multer(); const multerProcessor: Middleware<> = upload.array('multimedia'); @@ -111,28 +111,23 @@ async function multimediaUploadResponder( ); } -const uploadMediaMetadataInputValidator = tShape({ - filename: t.String, - width: t.Number, - height: t.Number, - blobHolder: t.String, - blobHash: t.String, - encryptionKey: t.String, - mimeType: t.String, - loop: t.maybe(t.Boolean), - thumbHash: t.maybe(t.String), -}); +export const uploadMediaMetadataInputValidator: TInterface = + tShape({ + filename: t.String, + width: t.Number, + height: t.Number, + blobHolder: t.String, + blobHash: t.String, + encryptionKey: t.String, + mimeType: t.String, + loop: t.maybe(t.Boolean), + thumbHash: t.maybe(t.String), + }); async function uploadMediaMetadataResponder( viewer: Viewer, - input: mixed, + request: UploadMediaMetadataRequest, ): Promise { - const request = await validateInput( - viewer, - uploadMediaMetadataInputValidator, - input, - ); - const mediaType = getMediaType(request.mimeType); if (!mediaType) { throw new ServerError('invalid_parameters'); @@ -160,11 +155,7 @@ async function uploadMediaMetadataResponder( }; const [result] = await createUploads(viewer, [uploadInfo]); - return validateOutput( - viewer.platformDetails, - uploadMultimediaResultValidator, - result, - ); + return result; } async function uploadDownloadResponder( @@ -233,20 +224,14 @@ async function uploadDownloadResponder( } } -const uploadDeletionRequestInputValidator: TInterface = +export const UploadDeletionRequestInputValidator: TInterface = tShape({ id: tID, }); async function uploadDeletionResponder( viewer: Viewer, - input: mixed, + { id }: UploadDeletionRequest, ): Promise { - const { id } = await validateInput( - viewer, - uploadDeletionRequestInputValidator, - input, - ); - await deleteUpload(viewer, id); } diff --git a/lib/types/activity-types.js b/lib/types/activity-types.js index dffee3bd2e..52282d04c2 100644 --- a/lib/types/activity-types.js +++ b/lib/types/activity-types.js @@ -46,7 +46,7 @@ export type SetThreadUnreadStatusRequest = { export type SetThreadUnreadStatusResult = { +resetToUnread: boolean, }; -export const setThreadUnreadStatusResult: TInterface = +export const setThreadUnreadStatusResultValidator: TInterface = tShape({ resetToUnread: t.Boolean }); export type SetThreadUnreadStatusPayload = { diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js index 1c83407d81..80c0fb9c16 100644 --- a/lib/types/crypto-types.js +++ b/lib/types/crypto-types.js @@ -1,5 +1,9 @@ // @flow +import t, { type TInterface } from 'tcomb'; + +import { tShape } from '../utils/validation-utils.js'; + export type OLMIdentityKeys = { +ed25519: string, +curve25519: string, @@ -37,6 +41,11 @@ export type SignedIdentityKeysBlob = { +payload: string, +signature: string, }; +export const signedIdentityKeysBlobValidator: TInterface = + tShape({ + payload: t.String, + signature: t.String, + }); // This type should not be changed without making equivalent changes to // `Message` in Identity service's `reserved_users` module diff --git a/lib/types/request-types.js b/lib/types/request-types.js index cdd593ced4..d9d85b0b0b 100644 --- a/lib/types/request-types.js +++ b/lib/types/request-types.js @@ -1,11 +1,12 @@ // @flow import invariant from 'invariant'; -import t, { type TUnion } from 'tcomb'; +import t, { type TUnion, type TInterface } from 'tcomb'; import { type ActivityUpdate } from './activity-types.js'; import type { Shape } from './core.js'; import type { SignedIdentityKeysBlob } from './crypto-types.js'; +import { signedIdentityKeysBlobValidator } from './crypto-types.js'; import type { Platform, PlatformDetails } from './device-types.js'; import { type RawEntryInfo, @@ -285,9 +286,21 @@ export type OlmSessionInitializationInfo = { +prekeySignature: string, +oneTimeKey: string, }; +export const olmSessionInitializationInfoValidator: TInterface = + tShape({ + prekey: t.String, + prekeySignature: t.String, + oneTimeKey: t.String, + }); export type GetOlmSessionInitializationDataResponse = { +signedIdentityKeysBlob: SignedIdentityKeysBlob, +contentInitializationInfo: OlmSessionInitializationInfo, +notifInitializationInfo: OlmSessionInitializationInfo, }; +export const getOlmSessionInitializationDataResponseValidator: TInterface = + tShape({ + signedIdentityKeysBlob: signedIdentityKeysBlobValidator, + contentInitializationInfo: olmSessionInitializationInfoValidator, + notifInitializationInfo: olmSessionInitializationInfoValidator, + });