diff --git a/.changeset/serious-garlics-clean.md b/.changeset/serious-garlics-clean.md new file mode 100644 index 00000000000..ccdc3c94dda --- /dev/null +++ b/.changeset/serious-garlics-clean.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/core-services': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +New AddUser workflow for Federated Rooms diff --git a/apps/meteor/app/api/server/index.ts b/apps/meteor/app/api/server/index.ts index 699bfdacb3a..00f1a62f1cb 100644 --- a/apps/meteor/app/api/server/index.ts +++ b/apps/meteor/app/api/server/index.ts @@ -46,6 +46,7 @@ import './v1/voip/extensions'; import './v1/voip/queues'; import './v1/voip/omnichannel'; import './v1/voip'; +import './v1/federation'; import './v1/moderation'; export { API, APIClass, defaultRateLimiterOptions } from './api'; diff --git a/apps/meteor/app/api/server/v1/federation.ts b/apps/meteor/app/api/server/v1/federation.ts new file mode 100644 index 00000000000..02fc30763ee --- /dev/null +++ b/apps/meteor/app/api/server/v1/federation.ts @@ -0,0 +1,24 @@ +import { Federation, FederationEE } from '@rocket.chat/core-services'; +import { isFederationVerifyMatrixIdProps } from '@rocket.chat/rest-typings'; + +import { isEnterprise } from '../../../../ee/app/license/server'; +import { API } from '../api'; + +API.v1.addRoute( + 'federation/matrixIds.verify', + { + authRequired: true, + validateParams: isFederationVerifyMatrixIdProps, + }, + { + async get() { + const { matrixIds } = this.queryParams; + + const federationService = isEnterprise() ? FederationEE : Federation; + + const results = await federationService.verifyMatrixIds(matrixIds); + + return API.v1.success({ results: Object.fromEntries(results) }); + }, + }, +); diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx new file mode 100644 index 00000000000..6703b14f250 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx @@ -0,0 +1,83 @@ +import { Modal, Button, Box, Icon } from '@rocket.chat/fuselage'; +import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ComponentProps, ReactElement } from 'react'; +import React from 'react'; +import { useForm } from 'react-hook-form'; + +type AddMatrixUsersModalProps = { + matrixIdVerifiedStatus: Map; + completeUserList: string[]; + onClose: () => void; + onSave: (args_0: any) => Promise; +}; + +type FormValues = { + usersToInvite: string[]; +}; + +const verificationStatusAsIcon = (verificationStatus: string) => { + if (verificationStatus === 'VERIFIED') { + return 'circle-check'; + } + + if (verificationStatus === 'UNVERIFIED') { + return 'circle-cross'; + } + + if (verificationStatus === 'UNABLE_TO_VERIFY') { + return 'circle-exclamation'; + } +}; + +const AddMatrixUsersModal = ({ onClose, matrixIdVerifiedStatus, onSave, completeUserList }: AddMatrixUsersModalProps): ReactElement => { + const dispatchToastMessage = useToastMessageDispatch(); + const usersToInvite = completeUserList.filter( + (user) => !(matrixIdVerifiedStatus.has(user) && matrixIdVerifiedStatus.get(user) === 'UNVERIFIED'), + ); + + const { handleSubmit } = useForm({ + defaultValues: { + usersToInvite, + }, + }); + + const onSubmit = (data: FormValues) => { + onSave({ users: data.usersToInvite }) + .then(onClose) + .catch((error) => dispatchToastMessage({ type: 'error', message: error as Error })); + }; + + const t = useTranslation(); + + return ( + + + + Sending Invitations + + + + + + + {[...matrixIdVerifiedStatus.entries()].map(([_matrixId, _verificationStatus]) => ( +
  • + {_matrixId}: ['name']} size='x20' /> +
  • + ))} +
    +
    +
    + + + + + + +
    + ); +}; + +export default AddMatrixUsersModal; diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/useAddMatrixUsers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/useAddMatrixUsers.tsx new file mode 100644 index 00000000000..8e2ba65e02c --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/useAddMatrixUsers.tsx @@ -0,0 +1,39 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import React from 'react'; + +import AddMatrixUsersModal from './AddMatrixUsersModal'; + +export type useAddMatrixUsersProps = { + handleSave: (args_0: any) => Promise; + users: string[]; +}; + +export const useAddMatrixUsers = () => { + const setModal = useSetModal(); + const dispatchToastMessage = useToastMessageDispatch(); + const handleClose = useMutableCallback(() => setModal(null)); + const dispatchVerifyEndpoint = useEndpoint('GET', '/v1/federation/matrixIds.verify'); + + return useMutation(async ({ users, handleSave }: useAddMatrixUsersProps) => { + try { + let matrixIdVerificationMap = new Map(); + const matrixIds = users.filter((user) => user.startsWith('@')); + const matrixIdsVerificationResponse = await dispatchVerifyEndpoint({ matrixIds }); + const { results: matrixIdsVerificationResults } = matrixIdsVerificationResponse; + matrixIdVerificationMap = new Map(Object.entries(matrixIdsVerificationResults)); + + setModal( + } + />, + ); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error as Error }); + } + }); +}; diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx index fc2a3a4e18a..7a8f0d1e699 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx @@ -19,6 +19,7 @@ import UserAutoCompleteMultiple from '../../../../../components/UserAutoComplete import UserAutoCompleteMultipleFederated from '../../../../../components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated'; import { useRoom } from '../../../contexts/RoomContext'; import { useRoomToolbox } from '../../../contexts/RoomToolboxContext'; +import { useAddMatrixUsers } from './AddMatrixUsers/useAddMatrixUsers'; type AddUsersProps = { rid: IRoom['_id']; @@ -37,6 +38,7 @@ const AddUsers = ({ rid, onClickBack, reload }: AddUsersProps): ReactElement => const { handleSubmit, control, + getValues, formState: { isDirty }, } = useForm({ defaultValues: { users: [] } }); @@ -51,6 +53,8 @@ const AddUsers = ({ rid, onClickBack, reload }: AddUsersProps): ReactElement => } }); + const addClickHandler = useAddMatrixUsers(); + return ( <> @@ -80,9 +84,24 @@ const AddUsers = ({ rid, onClickBack, reload }: AddUsersProps): ReactElement => - + {isRoomFederated(room) ? ( + + ) : ( + + )} diff --git a/apps/meteor/ee/server/local-services/federation/service.ts b/apps/meteor/ee/server/local-services/federation/service.ts index 9635381d923..a1272f5c866 100644 --- a/apps/meteor/ee/server/local-services/federation/service.ts +++ b/apps/meteor/ee/server/local-services/federation/service.ts @@ -198,6 +198,10 @@ export class FederationServiceEE extends AbstractBaseFederationServiceEE impleme ); } + public async verifyMatrixIds(matrixIds: string[]): Promise> { + return super.verifyMatrixIds(matrixIds); + } + static async createFederationService(): Promise { const federationService = new FederationServiceEE(); await federationService.initialize(); diff --git a/apps/meteor/server/services/federation/domain/IFederationBridge.ts b/apps/meteor/server/services/federation/domain/IFederationBridge.ts index 59ed3b631ee..06faada2d8e 100644 --- a/apps/meteor/server/services/federation/domain/IFederationBridge.ts +++ b/apps/meteor/server/services/federation/domain/IFederationBridge.ts @@ -81,6 +81,7 @@ export interface IFederationBridge { getRoomTopic(externalRoomId: string, externalUserId: string): Promise; setRoomName(externalRoomId: string, externalUserId: string, roomName: string): Promise; setRoomTopic(externalRoomId: string, externalUserId: string, roomTopic: string): Promise; + verifyInviteeIds(matrixIds: string[]): Promise>; getRoomData( externalUserId: string, externalRoomId: string, diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts index a9ab541afe7..9a7e6735196 100644 --- a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts +++ b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts @@ -16,6 +16,9 @@ import { RoomMembershipChangedEventType } from './definitions/events/RoomMembers import { MatrixEnumRelatesToRelType, MatrixEnumSendMessageType } from './definitions/events/RoomMessageSent'; import type { MatrixEventRoomNameChanged } from './definitions/events/RoomNameChanged'; import type { MatrixEventRoomTopicChanged } from './definitions/events/RoomTopicChanged'; +import { HttpStatusCodes } from './helpers/HtttpStatusCodes'; +import { extractUserIdAndHomeserverFromMatrixId } from './helpers/MatrixIdStringTools'; +import { VerificationStatus, MATRIX_USER_IN_USE } from './helpers/MatrixIdVerificationTypes'; let MatrixUserInstance: any; @@ -166,6 +169,41 @@ export class MatrixBridge implements IFederationBridge { } } + public async verifyInviteeIds(matrixIds: string[]): Promise> { + const matrixIdVerificationMap = new Map(); + const matrixIdsVerificationPromises = matrixIds.map((matrixId) => this.verifyInviteeId(matrixId)); + const matrixIdsVerificationPromiseResponse = await Promise.allSettled(matrixIdsVerificationPromises); + const matrixIdsVerificationFulfilledResults = matrixIdsVerificationPromiseResponse + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + + matrixIds.forEach((matrixId, idx) => matrixIdVerificationMap.set(matrixId, matrixIdsVerificationFulfilledResults[idx])); + return matrixIdVerificationMap; + } + + private async verifyInviteeId(externalInviteeId: string): Promise { + const [userId, homeserverUrl] = extractUserIdAndHomeserverFromMatrixId(externalInviteeId); + try { + const response = await fetch(`https://${homeserverUrl}/_matrix/client/v3/register/available`, { params: { username: userId } }); + + if (response.status === HttpStatusCodes.BAD_REQUEST) { + const responseBody = await response.json(); + + if (responseBody.errcode === MATRIX_USER_IN_USE) { + return VerificationStatus.VERIFIED; + } + } + + if (response.status === HttpStatusCodes.OK) { + return VerificationStatus.UNVERIFIED; + } + } catch (e) { + return VerificationStatus.UNABLE_TO_VERIFY; + } + + return VerificationStatus.UNABLE_TO_VERIFY; + } + public async createUser(username: string, name: string, domain: string, avatarUrl?: string): Promise { if (!MatrixUserInstance) { throw new Error('Error loading the Matrix User instance from the external library'); diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/RoomReceiver.ts b/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/RoomReceiver.ts index a9734462ce5..b307e6e85e6 100644 --- a/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/RoomReceiver.ts +++ b/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/RoomReceiver.ts @@ -32,18 +32,22 @@ import type { } from '../../definitions/events/RoomPowerLevelsChanged'; import type { MatrixEventRoomTopicChanged } from '../../definitions/events/RoomTopicChanged'; +/** @deprecated export from {@link ../../helpers/MatrixIdStringTools} instead */ export const removeExternalSpecificCharsFromExternalIdentifier = (matrixIdentifier = ''): string => { return matrixIdentifier.replace('@', '').replace('!', '').replace('#', ''); }; +/** @deprecated export from {@link ../../helpers/MatrixIdStringTools} instead */ export const formatExternalUserIdToInternalUsernameFormat = (matrixUserId = ''): string => { return matrixUserId.split(':')[0]?.replace('@', ''); }; export const isAnExternalIdentifierFormat = (identifier: string): boolean => identifier.includes(':'); +/** @deprecated export from {@link ../../helpers/MatrixIdStringTools} instead */ export const isAnExternalUserIdFormat = (userId: string): boolean => isAnExternalIdentifierFormat(userId) && userId.includes('@'); +/** @deprecated export from {@link ../../helpers/MatrixIdStringTools} instead */ export const extractServerNameFromExternalIdentifier = (identifier = ''): string => { const splitted = identifier.split(':'); diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/helpers/HtttpStatusCodes.ts b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/HtttpStatusCodes.ts new file mode 100644 index 00000000000..012aa0d5cd0 --- /dev/null +++ b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/HtttpStatusCodes.ts @@ -0,0 +1,58 @@ +export const enum HttpStatusCodes { + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + PROCESSING = 102, + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NON_AUTHORITATIVE_INFORMATION = 203, + NO_CONTENT = 204, + RESET_CONTENT = 205, + PARTIAL_CONTENT = 206, + MULTI_STATUS = 207, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + MOVED_TEMPORARILY = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + USE_PROXY = 305, + TEMPORARY_REDIRECT = 307, + PERMANENT_REDIRECT = 308, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, + FORBIDDEN = 403, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + PROXY_AUTHENTICATION_REQUIRED = 407, + REQUEST_TIMEOUT = 408, + CONFLICT = 409, + GONE = 410, + LENGTH_REQUIRED = 411, + PRECONDITION_FAILED = 412, + REQUEST_TOO_LONG = 413, + REQUEST_URI_TOO_LONG = 414, + UNSUPPORTED_MEDIA_TYPE = 415, + REQUESTED_RANGE_NOT_SATISFIABLE = 416, + EXPECTATION_FAILED = 417, + IM_A_TEAPOT = 418, + INSUFFICIENT_SPACE_ON_RESOURCE = 419, + METHOD_FAILURE = 420, + MISDIRECTED_REQUEST = 421, + UNPROCESSABLE_ENTITY = 422, + LOCKED = 423, + FAILED_DEPENDENCY = 424, + PRECONDITION_REQUIRED = 428, + TOO_MANY_REQUESTS = 429, + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + UNAVAILABLE_FOR_LEGAL_REASONS = 451, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503, + GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOT_SUPPORTED = 505, + INSUFFICIENT_STORAGE = 507, + NETWORK_AUTHENTICATION_REQUIRED = 511, +} diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdStringTools.ts b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdStringTools.ts new file mode 100644 index 00000000000..fdd7c904d67 --- /dev/null +++ b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdStringTools.ts @@ -0,0 +1,25 @@ +export const removeExternalSpecificCharsFromExternalIdentifier = (matrixId = ''): string => { + return matrixId.replace('@', '').replace('!', '').replace('#', ''); +}; + +export const formatExternalUserIdToInternalUsernameFormat = (matrixId = ''): string => { + return matrixId.split(':')[0]?.replace('@', ''); +}; + +export const formatExternalAliasIdToInternalFormat = (alias = ''): string => { + return alias.split(':')[0]?.replace('#', ''); +}; + +export const isAnExternalIdentifierFormat = (identifier: string): boolean => identifier.includes(':'); + +export const isAnExternalUserIdFormat = (userId: string): boolean => isAnExternalIdentifierFormat(userId) && userId.includes('@'); + +export const extractServerNameFromExternalIdentifier = (identifier = ''): string => { + const splitted = identifier.split(':'); + + return splitted.length > 1 ? splitted[1] : ''; +}; + +export const extractUserIdAndHomeserverFromMatrixId = (matrixId = ''): string[] => { + return matrixId.replace('@', '').split(':'); +}; diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdVerificationTypes.ts b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdVerificationTypes.ts new file mode 100644 index 00000000000..4fc48c12923 --- /dev/null +++ b/apps/meteor/server/services/federation/infrastructure/matrix/helpers/MatrixIdVerificationTypes.ts @@ -0,0 +1,7 @@ +export const enum VerificationStatus { + VERIFIED = 'VERIFIED', + UNVERIFIED = 'UNVERIFIED', + UNABLE_TO_VERIFY = 'UNABLE_TO_VERIFY', +} + +export const MATRIX_USER_IN_USE = 'M_USER_IN_USE'; diff --git a/apps/meteor/server/services/federation/service.ts b/apps/meteor/server/services/federation/service.ts index 7c37d4182ce..be154cee4a6 100644 --- a/apps/meteor/server/services/federation/service.ts +++ b/apps/meteor/server/services/federation/service.ts @@ -230,6 +230,10 @@ export abstract class AbstractFederationService extends ServiceClassInternal { protected async cleanUpHandlers(): Promise { this.internalQueueInstance.setHandler(this.noop.bind(this), this.PROCESSING_CONCURRENCY); } + + protected async verifyMatrixIds(matrixIds: string[]): Promise> { + return this.bridge.verifyInviteeIds(matrixIds); + } } abstract class AbstractBaseFederationService extends AbstractFederationService { @@ -317,6 +321,10 @@ export class FederationService extends AbstractBaseFederationService implements ); } + public async verifyMatrixIds(matrixIds: string[]): Promise> { + return super.verifyMatrixIds(matrixIds); + } + static async createFederationService(): Promise { const federationService = new FederationService(); await federationService.initialize(); diff --git a/apps/meteor/tests/unit/server/federation/infrastructure/matrix/Bridge.spec.ts b/apps/meteor/tests/unit/server/federation/infrastructure/matrix/Bridge.spec.ts index db9ee3134f1..b1b8ebf4241 100644 --- a/apps/meteor/tests/unit/server/federation/infrastructure/matrix/Bridge.spec.ts +++ b/apps/meteor/tests/unit/server/federation/infrastructure/matrix/Bridge.spec.ts @@ -1,10 +1,14 @@ import { expect } from 'chai'; import proxyquire from 'proxyquire'; +import { VerificationStatus } from '../../../../../../server/services/federation/infrastructure/matrix/helpers/MatrixIdVerificationTypes'; + +const fetchStub = { + serverFetch: () => Promise.resolve({}), +}; + const { MatrixBridge } = proxyquire.noCallThru().load('../../../../../../server/services/federation/infrastructure/matrix/Bridge', { - 'meteor/fetch': { - '@global': true, - }, + '@rocket.chat/server-fetch': fetchStub, }); describe('Federation - Infrastructure - Matrix - Bridge', () => { @@ -49,4 +53,38 @@ describe('Federation - Infrastructure - Matrix - Bridge', () => { expect(bridge.isRoomFromTheSameHomeserver('!room:server2.com', 'server.com')).to.be.false; }); }); + + describe('#verifyInviteeId()', () => { + it('should return `VERIFIED` when the matrixId exists', async () => { + fetchStub.serverFetch = () => Promise.resolve({ status: 400, json: () => Promise.resolve({ errcode: 'M_USER_IN_USE' }) }); + + const verificationStatus = await bridge.verifyInviteeId('@user:server.com'); + + expect(verificationStatus).to.be.equal(VerificationStatus.VERIFIED); + }); + + it('should return `UNVERIFIED` when the matrixId does not exists', async () => { + fetchStub.serverFetch = () => Promise.resolve({ status: 200, json: () => Promise.resolve({}) }); + + const verificationStatus = await bridge.verifyInviteeId('@user:server.com'); + + expect(verificationStatus).to.be.equal(VerificationStatus.UNVERIFIED); + }); + + it('should return `UNABLE_TO_VERIFY` when the fetch() call fails', async () => { + fetchStub.serverFetch = () => Promise.reject(new Error('Error')); + + const verificationStatus = await bridge.verifyInviteeId('@user:server.com'); + + expect(verificationStatus).to.be.equal(VerificationStatus.UNABLE_TO_VERIFY); + }); + + it('should return `UNABLE_TO_VERIFY` when an unexepected status comes', async () => { + fetchStub.serverFetch = () => Promise.resolve({ status: 500 }); + + const verificationStatus = await bridge.verifyInviteeId('@user:server.com'); + + expect(verificationStatus).to.be.equal(VerificationStatus.UNABLE_TO_VERIFY); + }); + }); }); diff --git a/packages/core-services/src/types/IFederationService.ts b/packages/core-services/src/types/IFederationService.ts index 89dc2ae7383..a30b0371782 100644 --- a/packages/core-services/src/types/IFederationService.ts +++ b/packages/core-services/src/types/IFederationService.ts @@ -2,6 +2,8 @@ import type { FederationPaginatedResult, IFederationPublicRooms } from '@rocket. export interface IFederationService { createDirectMessageRoomAndInviteUser(internalInviterId: string, internalRoomId: string, externalInviteeId: string): Promise; + + verifyMatrixIds(matrixIds: string[]): Promise>; } export interface IFederationJoinExternalPublicRoomInput { @@ -34,4 +36,6 @@ export interface IFederationServiceEE { scheduleJoinExternalPublicRoom(internalUserId: string, externalRoomId: string, roomName?: string, pageToken?: string): Promise; joinExternalPublicRoom(input: IFederationJoinExternalPublicRoomInput): Promise; + + verifyMatrixIds(matrixIds: string[]): Promise>; } diff --git a/packages/rest-typings/src/v1/federation/FederationVerifyMatrixIdProps.ts b/packages/rest-typings/src/v1/federation/FederationVerifyMatrixIdProps.ts new file mode 100644 index 00000000000..a63d37da07b --- /dev/null +++ b/packages/rest-typings/src/v1/federation/FederationVerifyMatrixIdProps.ts @@ -0,0 +1,22 @@ +import Ajv from 'ajv'; + +const ajv = new Ajv(); + +export type FederationVerifyMatrixIdProps = { + matrixIds: string[]; +}; + +const FederationVerifyMatrixIdPropsSchema = { + type: 'object', + properties: { + matrixIds: { + type: 'array', + items: [{ type: 'string' }], + uniqueItems: true, + }, + }, + additionalProperties: false, + required: ['matrixIds'], +}; + +export const isFederationVerifyMatrixIdProps = ajv.compile(FederationVerifyMatrixIdPropsSchema); diff --git a/packages/rest-typings/src/v1/federation/index.ts b/packages/rest-typings/src/v1/federation/index.ts index 05a1503098e..36a883c9d84 100644 --- a/packages/rest-typings/src/v1/federation/index.ts +++ b/packages/rest-typings/src/v1/federation/index.ts @@ -15,3 +15,4 @@ export * from './FederationJoinExternalPublicRoomProps'; export * from './FederationPublicRoomsProps'; export * from './FederationAddServerProps'; export * from './FederationRemoveServerProps'; +export * from './FederationVerifyMatrixIdProps'; diff --git a/packages/rest-typings/src/v1/federation/rooms.ts b/packages/rest-typings/src/v1/federation/rooms.ts index 2d3786a0757..ce467990aac 100644 --- a/packages/rest-typings/src/v1/federation/rooms.ts +++ b/packages/rest-typings/src/v1/federation/rooms.ts @@ -1,6 +1,7 @@ import type { FederationAddServerProps, FederationPaginatedResult, FederationRemoveServerProps } from '.'; import type { FederationJoinExternalPublicRoomProps } from './FederationJoinExternalPublicRoomProps'; import type { FederationSearchPublicRoomsProps } from './FederationPublicRoomsProps'; +import type { FederationVerifyMatrixIdProps } from './FederationVerifyMatrixIdProps'; export interface IFederationPublicRooms { id: string; @@ -30,4 +31,7 @@ export type FederationEndpoints = { '/v1/federation/removeServerByUser': { POST: (params: FederationRemoveServerProps) => void; }; + '/v1/federation/matrixIds.verify': { + GET: (params: FederationVerifyMatrixIdProps) => { results: Map }; + }; };