diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- 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 @@ 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 @@ 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 @@ 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 @@ 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 --- 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 @@ 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 @@ 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 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 --- a/keyserver/src/responders/device-responders.js +++ b/keyserver/src/responders/device-responders.js @@ -8,10 +8,9 @@ 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 @@ async function deviceTokenUpdateResponder( viewer: Viewer, - input: any, + request: DeviceTokenUpdateRequest, ): Promise { - 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 --- a/keyserver/src/responders/entry-responders.js +++ b/keyserver/src/responders/entry-responders.js @@ -46,7 +46,6 @@ 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 @@ +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 @@ async function entryFetchResponder( viewer: Viewer, - input: mixed, + inputQuery: EntryQueryInput, ): Promise { - 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 @@ async function entryRevisionFetchResponder( viewer: Viewer, - input: mixed, + request: FetchEntryRevisionInfosRequest, ): Promise { - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- 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 @@ 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 @@ } export { + createJSONResponder, jsonHandler, httpGetHandler, downloadHandler, 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 @@ -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 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 @@ async function getSessionPublicKeysResponder( viewer: Viewer, - input: mixed, + request: GetSessionPublicKeysArgs, ): Promise { 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 --- a/keyserver/src/responders/link-responders.js +++ b/keyserver/src/responders/link-responders.js @@ -9,6 +9,7 @@ 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 @@ 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 @@ async function inviteLinkVerificationResponder( viewer: Viewer, - input: mixed, + request: InviteLinkVerificationRequest, ): Promise { - 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 @@ 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 --- a/keyserver/src/responders/message-report-responder.js +++ b/keyserver/src/responders/message-report-responder.js @@ -11,9 +11,8 @@ 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 @@ async function messageReportCreationResponder( viewer: Viewer, - input: mixed, + request: MessageReportCreationRequest, ): Promise { - 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 --- 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 @@ 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 @@ } 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 @@ async function messageFetchResponder( viewer: Viewer, - input: mixed, + request: FetchMessageInfosRequest, ): Promise { - 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 @@ ]); 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 @@ ); } - 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 @@ }); 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 @@ 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 @@ async function editMessageCreationResponder( viewer: Viewer, - input: mixed, + request: SendEditMessageRequest, ): Promise { - const request = await validateInput( - viewer, - editMessageRequestInputValidator, - input, - ); - const { targetMessageID, text: rawText } = request; const text = trimMessage(rawText); if (!targetMessageID || !text) { @@ -444,15 +396,10 @@ 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 @@ async function fetchPinnedMessagesResponder( viewer: Viewer, - input: mixed, + request: FetchPinnedMessagesRequest, ): Promise { - 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 @@ 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 --- a/keyserver/src/responders/relationship-responders.js +++ b/keyserver/src/responders/relationship-responders.js @@ -11,12 +11,12 @@ 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 @@ 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 --- 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 @@ 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 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 @@ 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 @@ }; 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 @@ ); } -const fetchErrorReportInfosRequestInputValidator = +export const fetchErrorReportInfosRequestInputValidator: TInterface = tShape({ cursor: t.maybe(t.String), }); @@ -227,19 +213,9 @@ async function errorReportFetchInfosResponder( viewer: Viewer, - input: mixed, + request: FetchErrorReportInfosRequest, ): Promise { - 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 --- 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 @@ 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 --- a/keyserver/src/responders/search-responders.js +++ b/keyserver/src/responders/search-responders.js @@ -13,11 +13,11 @@ 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 @@ 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 --- a/keyserver/src/responders/siwe-nonce-responders.js +++ b/keyserver/src/responders/siwe-nonce-responders.js @@ -7,18 +7,16 @@ 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 --- a/keyserver/src/responders/thread-responders.js +++ b/keyserver/src/responders/thread-responders.js @@ -62,12 +62,12 @@ 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 @@ 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 @@ async function roleUpdateResponder( viewer: Viewer, - input: mixed, + request: RoleChangeRequest, ): Promise { - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- 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 @@ type ClientAvatar, clientAvatarValidator, type UpdateUserAvatarResponse, + type UpdateUserAvatarRequest, } from 'lib/types/avatar-types.js'; import type { IdentityKeysBlob, @@ -66,7 +67,6 @@ oldLoggedInUserInfoValidator, userInfoValidator, } from 'lib/types/user-types.js'; -import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js'; import { identityKeysBlobValidator, signedIdentityKeysBlobValidator, @@ -132,9 +132,8 @@ 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 @@ async function userSubscriptionUpdateResponder( viewer: Viewer, - input: mixed, + request: SubscriptionUpdateRequest, ): Promise { - 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 @@ 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 @@ ]); 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } // 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 @@ updates: createUpdatesResultValidator, }); -const updateUserAvatarResponderValidator = t.union([ +export const updateUserAvatarResponderValidator: TUnion< + ?ClientAvatar | UpdateUserAvatarResponse, +> = 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 --- 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 --- a/keyserver/src/responders/version-responders.js +++ b/keyserver/src/responders/version-responders.js @@ -6,20 +6,13 @@ 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 --- a/keyserver/src/uploads/uploads.js +++ b/keyserver/src/uploads/uploads.js @@ -27,7 +27,7 @@ } 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 @@ ); } -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 @@ }; const [result] = await createUploads(viewer, [uploadInfo]); - return validateOutput( - viewer.platformDetails, - uploadMultimediaResultValidator, - result, - ); + return result; } async function uploadDownloadResponder( @@ -233,20 +224,14 @@ } } -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 --- a/lib/types/activity-types.js +++ b/lib/types/activity-types.js @@ -46,7 +46,7 @@ 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 --- 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 @@ +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 --- 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 @@ +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, + });