diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -19,6 +19,7 @@ } from './responders/entry-responders.js'; import type { JSONResponder } from './responders/handlers.js'; import { getSessionPublicKeysResponder } from './responders/keys-responders.js'; +import { inviteLinkVerificationResponder } from './responders/link-responders.js'; import { messageReportCreationResponder } from './responders/message-report-responder.js'; import { textMessageCreationResponder, @@ -250,6 +251,10 @@ responder: codeVerificationResponder, requiredPolicies: baseLegalPolicies, }, + verify_invite_link: { + responder: inviteLinkVerificationResponder, + requiredPolicies: baseLegalPolicies, + }, siwe_nonce: { responder: siweNonceResponder, requiredPolicies: [], diff --git a/keyserver/src/fetchers/link-fetchers.js b/keyserver/src/fetchers/link-fetchers.js new file mode 100644 --- /dev/null +++ b/keyserver/src/fetchers/link-fetchers.js @@ -0,0 +1,42 @@ +// @flow + +import type { + InviteLinkVerificationRequest, + InviteLinkVerificationResponse, +} from 'lib/types/link-types.js'; + +import { dbQuery, SQL } from '../database/database.js'; +import { Viewer } from '../session/viewer.js'; + +async function verifyInviteLink( + viewer: Viewer, + request: InviteLinkVerificationRequest, +): Promise { + const query = SQL` + SELECT c.name, i.community AS communityID, m.role + FROM invite_links i + INNER JOIN threads c ON c.id = i.community + LEFT JOIN memberships m + ON m.thread = i.community AND m.user = ${viewer.userID} + WHERE i.name = ${request.secret} + AND c.community IS NULL + `; + const [result] = await dbQuery(query); + if (result.length === 0) { + return { + status: 'invalid', + }; + } + + const { name, communityID, role } = result[0]; + const status = role > 0 ? 'already_joined' : 'valid'; + return { + status, + community: { + name, + id: communityID.toString(), + }, + }; +} + +export { verifyInviteLink }; diff --git a/keyserver/src/responders/link-responders.js b/keyserver/src/responders/link-responders.js new file mode 100644 --- /dev/null +++ b/keyserver/src/responders/link-responders.js @@ -0,0 +1,32 @@ +// @flow + +import t from 'tcomb'; + +import type { + InviteLinkVerificationRequest, + InviteLinkVerificationResponse, +} from 'lib/types/link-types.js'; +import { tShape } from 'lib/utils/validation-utils.js'; + +import { verifyInviteLink } from '../fetchers/link-fetchers.js'; +import { Viewer } from '../session/viewer.js'; +import { validateInput } from '../utils/validation-utils.js'; + +const inviteLinkVerificationRequestInputValidator = tShape({ + secret: t.String, +}); + +async function inviteLinkVerificationResponder( + viewer: Viewer, + input: any, +): Promise { + const request: InviteLinkVerificationRequest = input; + await validateInput( + viewer, + inviteLinkVerificationRequestInputValidator, + request, + ); + return await verifyInviteLink(viewer, request); +} + +export { inviteLinkVerificationResponder }; diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js --- a/lib/types/endpoints.js +++ b/lib/types/endpoints.js @@ -84,6 +84,7 @@ UPDATE_THREAD: 'update_thread', UPDATE_USER_SUBSCRIPTION: 'update_user_subscription', VERIFY_CODE: 'verify_code', + VERIFY_INVITE_LINK: 'verify_invite_link', SIWE_NONCE: 'siwe_nonce', SIWE_AUTH: 'siwe_auth', UPDATE_USER_AVATAR: 'update_user_avatar', diff --git a/lib/types/link-types.js b/lib/types/link-types.js new file mode 100644 --- /dev/null +++ b/lib/types/link-types.js @@ -0,0 +1,17 @@ +// @flow + +export type InviteLinkVerificationRequest = { + +secret: string, +}; + +export type InviteLinkVerificationResponse = + | { + +status: 'valid' | 'already_joined', + +community: { + +name: string, + +id: string, + }, + } + | { + +status: 'invalid' | 'expired', + };