Skip to content

Commit

Permalink
processSendJoinResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
ggazzo committed Dec 27, 2024
1 parent 6ae783e commit 45a410a
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 110 deletions.
177 changes: 177 additions & 0 deletions packages/homeserver/src/procedures/processSendJoinResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import type { EventBase } from "@hs/core/src/events/eventBase";
import { generateId, type HashedEvent } from "../authentication";
import type { SignedEvent } from "../signJson";
import { checkSignAndHashes } from "../routes/federation/checkSignAndHashes";
import {
checkEventAuthorization,
ensureAuthorizationRulesAndStoreBatch,
} from "./autorization/ensureAuthorizationRules";
import { MatrixError } from "../errors";
import type { EventStore } from "../plugins/mongodb";
import { makeRequest } from "../makeRequest";

const loadOrFetchAuthEventsForPDU = async (
pdu: SignedEvent<HashedEvent<EventBase>>,
claimedAuthEventsId: string[],
events: {
insertMany: (events: EventStore[]) => Promise<void>;
insertOne: (event: EventStore) => Promise<void>;
upsertRoom: (roomId: string, state: EventBase[]) => Promise<void>;
getByIds: (roomId: string, eventIds: string[]) => Promise<EventStore[]>;
},
) => {
const claimedAuthEventsFromDb = await events.getByIds(
pdu.room_id,
claimedAuthEventsId,
);

const claimedAuthEvents = new Map(
claimedAuthEventsFromDb.map((event) => [event._id, event.event]),
);

const missingAuthEvents = claimedAuthEventsId.filter(
(eventId) => !claimedAuthEvents.get(eventId),
);

if (!missingAuthEvents.length) {
return claimedAuthEvents;
}

const eventId = generateId(pdu);

const { auth_chain } = await makeRequest({
method: "GET",
domain: pdu.origin,
uri: `/_matrix/federation/v1/event_auth/${pdu.room_id}/${eventId}`,
signingName: "asd",
});

for (const event of auth_chain) {
claimedAuthEvents.set(generateId(event), event);
}

const missingAuthEventsFromRemote = missingAuthEvents.filter(
(eventId) => !claimedAuthEvents.get(eventId),
);

if (!missingAuthEventsFromRemote.length) {
return claimedAuthEvents;
}

throw new MatrixError("400", "Missing auth events");
};

export const processSendJoinResponse = async (
keys: {
getPublicKeyFromServer: (origin: string, key: string) => Promise<string>;
},
events: {
insertMany: (events: EventStore[]) => Promise<void>;
insertOne: (event: EventStore) => Promise<void>;
upsertRoom: (roomId: string, state: EventBase[]) => Promise<void>;
getByIds: (roomId: string, eventIds: string[]) => Promise<EventStore[]>;
},
pdu: SignedEvent<HashedEvent<EventBase>>,
state: EventBase[],
authChain: EventBase[],
) => {
await authAndPersistOutliers(state, authChain, keys, events, pdu);

const claimedAuthEvents = await loadOrFetchAuthEventsForPDU(
pdu,
pdu.auth_events,
events,
);

if (await checkEventAuthorization(pdu, claimedAuthEvents)) {
await events.insertOne({
_id: generateId(pdu),
event: pdu,
});
}

await ensureAuthorizationRulesAndStoreBatch(
events,
[...claimedAuthEvents.values()],
pdu.room_id,
100,
);
};

async function authAndPersistOutliers(
state: EventBase[],
authChain: EventBase[],
keys: {
getPublicKeyFromServer: (origin: string, key: string) => Promise<string>;
},
events: {
insertMany: (events: EventStore[]) => Promise<void>;
insertOne: (event: EventStore) => Promise<void>;
upsertRoom: (roomId: string, state: EventBase[]) => Promise<void>;
getByIds: (roomId: string, eventIds: string[]) => Promise<EventStore[]>;
},
pdu: SignedEvent<HashedEvent<EventBase>>,
) {
const createEvent = state.find((event) => event.type === "m.room.create");

if (!createEvent) {
throw new MatrixError("400", "Invalid response");
}

const auth_chain = new Map(
authChain.map((event) => [generateId(event), event]),
);

const states = new Map(state.map((event) => [generateId(event), event]));

const validPDUs = new Map<string, EventBase>();

for await (const [eventId, event] of [
...auth_chain.entries(),
...states.entries(),
]) {
// check sign and hash of event
if (
await checkSignAndHashes(
event,
event.origin,
keys.getPublicKeyFromServer,
).catch((e) => {
console.log("Error checking signature", e);
return false;
})
) {
validPDUs.set(eventId, event);
} else {
console.log("Invalid event", event);
}
}

const signedAuthChain = [...auth_chain.entries()].filter(([eventId]) =>
validPDUs.has(eventId),
);

const signedState = [...states.entries()].filter(([eventId]) =>
validPDUs.has(eventId),
);

const signedCreateEvent = signedAuthChain.find(
([, event]) => event.type === "m.room.create",
);

if (!signedCreateEvent) {
throw new MatrixError("400", "Unexpected create event(s) in auth chain");
}

// TODO: this should be placed in a different moment
await events.upsertRoom(
signedCreateEvent[1].room_id,
signedState.map(([, event]) => event),
);

await ensureAuthorizationRulesAndStoreBatch(
events,
signedAuthChain.map(([, event]) => event),
pdu.room_id,
);
}
130 changes: 20 additions & 110 deletions packages/homeserver/src/routes/federation/sendInviteV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
getPublicKeyFromRemoteServer,
makeGetPublicKeyFromServerProcedure,
} from "../../procedures/getPublicKeyFromServer";
import { checkSignAndHashes } from "./checkSignAndHashes";
import { processSendJoinResponse } from "../../procedures/processSendJoinResponse";

export const sendInviteV2Route = new Elysia().put(
"/invite/:roomId/:eventId",
Expand All @@ -31,13 +31,6 @@ export const sendInviteV2Route = new Elysia().put(
mongo: { eventsCollection, upsertRoom },
} = context;

console.log("invite received ->", { params, body });

await eventsCollection.insertOne({
_id: generateId(body.event),
event: body.event,
});

setTimeout(async () => {
const { event } = body;

Expand All @@ -52,19 +45,6 @@ export const sendInviteV2Route = new Elysia().put(

console.log("make_join response ->", responseMake);

// const joinBody = {
// type: 'm.room.member',
// origin: config.name,
// origin_server_ts: Date.now(),
// room_id: responseMake.event.room_id,
// state_key: responseMake.event.state_key,
// sender: responseMake.event.sender,
// depth: responseMake.event.depth + 1,
// content: {
// membership: 'join'
// }
// };

const responseBody = await makeSignedRequest({
method: "PUT",
domain: event.origin,
Expand All @@ -80,102 +60,32 @@ export const sendInviteV2Route = new Elysia().put(
queryString: "omit_members=false",
});

console.log("send_join response ->", { responseBody });

const { event: pdu, origin } = responseBody;

const createEvent = responseBody.state.find(
(event) => event.type === "m.room.create",
);

if (!createEvent) {
throw new MatrixError("400", "Invalid response");
}

if (pdu) {
await eventsCollection.insertOne({
_id: generateId(responseBody.event),
event: responseBody.event,
});
}

const auth_chain = new Map(
responseBody.auth_chain.map((event) => [generateId(event), event]),
);

const state = new Map(
responseBody.state.map((event) => [generateId(event), event]),
);

const getPublicKeyFromServer = makeGetPublicKeyFromServerProcedure(
context.mongo.getValidPublicKeyFromLocal,
(origin, key) => getPublicKeyFromRemoteServer(origin, config.name, key),

context.mongo.storePublicKey,
);

const validPDUs = new Map<string, EventBase>();

for await (const [eventId, event] of [
...auth_chain.entries(),
...state.entries(),
]) {
// check sign and hash of event
if (
await checkSignAndHashes(
event as SignedJson<HashedEvent<EventBase>>,
event.origin,
getPublicKeyFromServer,
).catch((e) => {
console.log("Error checking signature", e);
return false;
})
) {
validPDUs.set(eventId, event);
} else {
console.log("Invalid event", event);
}
}

const signedAuthChain = [...auth_chain.entries()].filter(([eventId]) =>
validPDUs.has(eventId),
);

const signedState = [...state.entries()].filter(([eventId]) =>
validPDUs.has(eventId),
);

const signedCreateEvent = signedAuthChain.find(
([, event]) => event.type === "m.room.create",
);

if (!signedCreateEvent) {
console.log("Invalid create event", validPDUs);
throw new MatrixError(
"400",
"Unexpected create event(s) in auth chain",
);
}

await upsertRoom(
signedCreateEvent[1].room_id,
signedState.map(([, event]) => event),
);

await Promise.all(
signedState.map(([eventId, event]) => {
const promise = eventsCollection
.insertOne({
_id: eventId,
event,
})
.catch((e) => {
// TODO events failing because of duplicate key
// the reason is that we are saving the event on invite event
console.error("error saving event", e, event);
});
return promise;
}) ?? [],
return processSendJoinResponse(
{
getPublicKeyFromServer,
},
{
insertMany: async (...args) => {
await eventsCollection.insertMany(...args);
},
insertOne: async (...args) => {
await eventsCollection.insertOne(...args);
},
upsertRoom,
getByIds: async (...args) => {
throw new Error("Method not implemented.");
},
},
responseBody.event,
responseBody.state,
responseBody.auth_chain,
);
}, 1000);

Expand Down

0 comments on commit 45a410a

Please sign in to comment.