Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update main branch to current v0.6.5 pre-release state #7

Merged
merged 22 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7b74b2d
Refresh joined rooms when enabling crypto (#281)
AndrewFerr Dec 19, 2022
38d4cc4
Use the transaction ID for the request ID in a to-device request (#288)
turt2live Dec 19, 2022
c3bf77c
Query & claim needed keys before encrypting (#270)
AndrewFerr Dec 19, 2022
e055ca6
Update sanitize-html
turt2live Dec 19, 2022
b8bcd76
v0.6.3
turt2live Dec 19, 2022
8dd84e9
Reset version back to "develop" for jsdoc, for now
turt2live Dec 19, 2022
5c7fbdf
Always send reactions unencrypted (#290)
turt2live Dec 30, 2022
c7d1677
Bump json5 from 1.0.1 to 1.0.2 (#292)
dependabot[bot] Jan 8, 2023
b9911ef
Add "reason" to leaveRoom. (#301)
Half-Shot Mar 19, 2023
8f7ef01
Permalinks should be able to decode and encode via servers. (#300)
Gnuxie Mar 19, 2023
bc7b9f9
Mark users as tracked before encrypting to them (#303)
turt2live Mar 20, 2023
e914f8b
v0.6.4
turt2live Mar 20, 2023
55c2dbf
Reset version back to "develop" for jsdoc, for now
turt2live Mar 20, 2023
e009ae7
Expose MSC3202 extensions over the appservice EventEmitter (#306)
turt2live Mar 23, 2023
fb121c2
Support MSC3983: Keys Claim request (#307)
turt2live Mar 23, 2023
34ee889
Correctly detect Authorization header from appservice API requests (#…
turt2live Mar 24, 2023
d4b6a6b
Return 404 M_UNRECOGNIZED per spec on unknown endpoints (#308)
turt2live Mar 24, 2023
7f09008
Start a sync after creating a new filter (#297)
dhenneke Mar 24, 2023
3238da3
Fix a couple issues related to crypto handling on toDevice (#310)
turt2live Mar 24, 2023
504731a
Support MSC3984: Keys Query request (#311)
turt2live Mar 24, 2023
1ae614f
Pin rust-sdk crypto dependency (#312)
turt2live Mar 24, 2023
591b66e
Merge branch 'main' into travis/update
turt2live Mar 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion examples/encryption_appservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const worksImage = fs.readFileSync("./examples/static/it-works.png");
const registration: IAppserviceRegistration = {
"as_token": creds?.['asToken'] ?? "change_me",
"hs_token": creds?.['hsToken'] ?? "change_me",
"sender_localpart": "crypto_main_bot_user",
"sender_localpart": "crypto_main_bot_user2",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

random 2 though? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upstream thing - the example fields change constantly because they're also debug scripts.

"namespaces": {
users: [{
regex: "@crypto.*:localhost",
Expand Down Expand Up @@ -95,6 +95,16 @@ const bot = appservice.botIntent;
});
}

appservice.on("query.key_claim", (req, done) => {
LogService.info("index", "Key claim request:", req);
done({});
});

appservice.on("query.key", (req, done) => {
LogService.info("index", "Key query request:", req);
done({});
});

appservice.on("room.failed_decryption", async (roomId: string, event: any, e: Error) => {
LogService.error("index", `Failed to decrypt ${roomId} ${event['event_id']} because `, e);
});
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"tsconfig.json"
],
"dependencies": {
"@matrix-org/matrix-sdk-crypto-nodejs": "^0.1.0-beta.3",
"@matrix-org/matrix-sdk-crypto-nodejs": "0.1.0-beta.3",
"@types/express": "^4.17.13",
"another-json": "^0.2.0",
"async-lock": "^1.3.2",
Expand All @@ -67,7 +67,7 @@
"morgan": "^1.10.0",
"request": "^2.88.2",
"request-promise": "^4.2.6",
"sanitize-html": "^2.7.0"
"sanitize-html": "^2.8.0"
},
"devDependencies": {
"@babel/core": "^7.18.2",
Expand Down
7 changes: 4 additions & 3 deletions src/MatrixClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ export class MatrixClient extends EventEmitter {

if (createFilter && filter) {
LogService.trace("MatrixClientLite", "Creating new filter");
return this.doRequest("POST", "/_matrix/client/v3/user/" + encodeURIComponent(userId) + "/filter", null, filter).then(async response => {
await this.doRequest("POST", "/_matrix/client/v3/user/" + encodeURIComponent(userId) + "/filter", null, filter).then(async response => {
this.filterId = response["filter_id"];
// noinspection ES6RedundantAwait
await Promise.resolve(this.storage.setSyncToken(null));
Expand Down Expand Up @@ -1162,11 +1162,12 @@ export class MatrixClient extends EventEmitter {
/**
* Leaves the given room
* @param {string} roomId the room ID to leave
* @param {string=} reason Optional reason to be included as the reason for leaving the room.
* @returns {Promise<any>} resolves when left
*/
@timedMatrixClientFunctionCall()
public leaveRoom(roomId: string): Promise<any> {
return this.doRequest("POST", "/_matrix/client/v3/rooms/" + encodeURIComponent(roomId) + "/leave", null, {});
public leaveRoom(roomId: string, reason?: string): Promise<any> {
return this.doRequest("POST", "/_matrix/client/v3/rooms/" + encodeURIComponent(roomId) + "/leave", null, { reason });
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/UnstableApis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class UnstableApis {
* @returns {Promise<string>} Resolves to the event ID of the reaction
*/
public async addReactionToEvent(roomId: string, eventId: string, emoji: string): Promise<string> {
return this.client.sendEvent(roomId, "m.reaction", {
return this.client.sendRawEvent(roomId, "m.reaction", {
"m.relates_to": {
event_id: eventId,
key: emoji,
Expand Down
242 changes: 168 additions & 74 deletions src/appservice/Appservice.ts

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/appservice/Intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class Intent {
}

// Now set up crypto
await this.client.crypto.prepare(await this.client.getJoinedRooms());
await this.client.crypto.prepare(await this.refreshJoinedRooms());

this.appservice.on("room.event", (roomId, event) => {
if (!this.knownJoinedRooms.includes(roomId)) return;
Expand Down Expand Up @@ -201,12 +201,13 @@ export class Intent {
/**
* Leaves the given room.
* @param {string} roomId The room ID to leave
* @param {string=} reason Optional reason to be included as the reason for leaving the room.
* @returns {Promise<any>} Resolves when the room has been left.
*/
@timedIntentFunctionCall()
public async leaveRoom(roomId: string): Promise<any> {
public async leaveRoom(roomId: string, reason?: string): Promise<any> {
await this.ensureRegistered();
return this.client.leaveRoom(roomId).then(async () => {
return this.client.leaveRoom(roomId, reason).then(async () => {
// Recalculate joined rooms now that we've left a room
await this.refreshJoinedRooms();
});
Expand Down
53 changes: 53 additions & 0 deletions src/appservice/http_responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,56 @@ interface IProtocolInstance {
fields: { [field: string]: string };
network_id: string;
}

/**
* This is the response format for an MSC3983 `/keys/claim` request.
* See https://github.com/matrix-org/matrix-spec-proposals/pull/3983
* @deprecated This can be removed at any time without notice as it is unstable functionality.
* @category Application services
*/
export interface MSC3983KeyClaimResponse {
[userId: string]: {
[deviceId: string]: {
[keyId: string]: {
// for signed_curve25519 keys
key: string;
signatures: {
[userId: string]: {
[keyId: string]: string;
};
};
};
};
};
}

/**
* This is the response format for an MSC3984 `/keys/query` request.
* See https://github.com/matrix-org/matrix-spec-proposals/pull/3984
* @deprecated This can be removed at any time without notice as it is unstable functionality.
* @category Application services
*/
export interface MSC3984KeyQueryResponse {
device_keys: {
[userId: string]: {
[deviceId: string]: {
algorithms: string[];
device_id: string;
user_id: string;
keys: {
[keyId: string]: string;
};
signatures: {
[userId: string]: {
[keyId: string]: string;
};
};
unsigned?: {
[key: string]: any;
};
};
};
};

// TODO: Cross-signing support
}
17 changes: 12 additions & 5 deletions src/e2ee/RustEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export class RustEngine {
public constructor(public readonly machine: OlmMachine, private client: MatrixClient) {
}

public async run(...types: RequestType[]) {
public async run() {
await this.runOnly(); // run everything, but with syntactic sugar
}

private async runOnly(...types: RequestType[]) {
// Note: we should not be running this until it runs out, so cache the value into a variable
const requests = await this.machine.outgoingRequests();
for (const request of requests) {
Expand Down Expand Up @@ -122,9 +126,10 @@ export class RustEngine {
settings.rotationPeriod = BigInt(encEv.rotationPeriodMs);
settings.rotationPeriodMessages = BigInt(encEv.rotationPeriodMessages);

await this.run(RequestType.KeysQuery);
await this.lock.acquire(SYNC_LOCK_NAME, async () => {
const keysClaim = await this.machine.getMissingSessions(membersArray);
await this.machine.updateTrackedUsers(members); // just in case we missed some
await this.runOnly(RequestType.KeysQuery);
const keysClaim = await this.machine.getMissingSessions(members);
if (keysClaim) {
await this.processKeysClaimRequest(keysClaim);
}
Expand All @@ -144,7 +149,9 @@ export class RustEngine {
}

private async processKeysUploadRequest(request: KeysUploadRequest) {
const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/upload", null, JSON.parse(request.body));
const body = JSON.parse(request.body);
// delete body["one_time_keys"]; // use this to test MSC3983
const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/upload", null, body);
await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
}

Expand All @@ -155,7 +162,7 @@ export class RustEngine {

private async processToDeviceRequest(request: ToDeviceRequest) {
const req = JSON.parse(request.body);
await this.actuallyProcessToDeviceRequest(req.id, req.event_type, req.messages);
await this.actuallyProcessToDeviceRequest(req.txn_id, req.event_type, req.messages);
}

private async actuallyProcessToDeviceRequest(id: string, type: string, messages: Record<string, Record<string, unknown>>) {
Expand Down
43 changes: 18 additions & 25 deletions src/helpers/Permalinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
*/
export interface PermalinkParts {
/**
* The room ID or alias the permalink references. May be null.
* The room ID or alias the permalink references. May be undefined.
*/
roomIdOrAlias: string;

/**
* The user ID the permalink references. May be null.
* The user ID the permalink references. May be undefined.
*/
userId: string;

/**
* The event ID the permalink references. May be null.
* The event ID the permalink references. May be undefined.
*/
eventId: string;

/**
* The servers the permalink is routed through. May be null or empty.
* The servers the permalink is routed through. May be undefined or empty.
*/
viaServers: string[];
}
Expand All @@ -38,7 +38,7 @@ export class Permalinks {
private static encodeViaArgs(servers: string[]): string {
if (!servers || !servers.length) return "";

return `?via=${servers.join("via=")}`;
return `?via=${servers.join("&via=")}`;
}

/**
Expand Down Expand Up @@ -77,32 +77,25 @@ export class Permalinks {
* @returns {PermalinkParts} The parts of the permalink.
*/
public static parseUrl(matrixTo: string): PermalinkParts {
if (!matrixTo || !matrixTo.startsWith("https://matrix.to/#/")) {
const matrixToRegexp = /^https:\/\/matrix\.to\/#\/(?<entity>[^/?]+)\/?(?<eventId>[^?]+)?(?<query>\?[^]*)?$/;

const url = matrixToRegexp.exec(matrixTo)?.groups;
if (!url) {
throw new Error("Not a valid matrix.to URL");
}

const parts = matrixTo.substring("https://matrix.to/#/".length).split("/");

const entity = decodeURIComponent(parts[0]);
const entity = decodeURIComponent(url.entity);
if (entity[0] === '@') {
return { userId: entity, roomIdOrAlias: undefined, eventId: undefined, viaServers: undefined };
} else if (entity[0] === '#' || entity[0] === '!') {
if (parts.length === 1) {
return { roomIdOrAlias: entity, userId: undefined, eventId: undefined, viaServers: [] };
}

// rejoin the rest because v3 room can have slashes
const eventIdAndQuery = decodeURIComponent(parts.length > 1 ? parts.slice(1).join('/') : "");
const secondaryParts = eventIdAndQuery.split('?');

const eventId = secondaryParts[0];
const query = secondaryParts.length > 1 ? secondaryParts[1] : "";

const via = query.split("via=").filter(p => !!p);

return { roomIdOrAlias: entity, eventId: eventId, viaServers: via, userId: undefined };
return {
userId: undefined,
roomIdOrAlias: entity,
eventId: url.eventId && decodeURIComponent(url.eventId),
viaServers: new URLSearchParams(url.query).getAll('via'),
};
} else {
throw new Error("Unexpected entity");
}

throw new Error("Unexpected entity");
}
}
31 changes: 29 additions & 2 deletions test/MatrixClientTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1177,10 +1177,16 @@ describe('MatrixClient', () => {
http.when("POST", "/_matrix/client/v3/user").respond(200, (path, content) => {
expect(path).toEqual(`${hsUrl}/_matrix/client/v3/user/${encodeURIComponent(userId)}/filter`);
expect(content).toMatchObject(filter);
client.stop(); // avoid a sync early
return { filter_id: filterId };
});

// noinspection TypeScriptValidateJSTypes
http.when("GET", "/_matrix/client/v3/sync").respond(200, (path, content, req) => {
expect(req.queryParams.filter).toBe(filterId);
client.stop();
return { next_batch: "123" };
});

await Promise.all([client.start(filter), http.flushAllExpected()]);
expect(setFilterFn.callCount).toBe(1);
expect(dmsMock.callCount).toBe(1);
Expand Down Expand Up @@ -1215,10 +1221,16 @@ describe('MatrixClient', () => {
http.when("POST", "/_matrix/client/v3/user").respond(200, (path, content) => {
expect(path).toEqual(`${hsUrl}/_matrix/client/v3/user/${encodeURIComponent(userId)}/filter`);
expect(content).toMatchObject(filter);
client.stop(); // avoid a sync early
return { filter_id: filterId };
});

// noinspection TypeScriptValidateJSTypes
http.when("GET", "/_matrix/client/v3/sync").respond(200, (path, content, req) => {
expect(req.queryParams.filter).toBe(filterId);
client.stop();
return { next_batch: "123" };
});

await Promise.all([client.start(filter), http.flushAllExpected()]);
expect(getFilterFn.callCount).toBe(1);
expect(setFilterFn.callCount).toBe(1);
Expand Down Expand Up @@ -3340,6 +3352,21 @@ describe('MatrixClient', () => {

await Promise.all([client.leaveRoom(roomId), http.flushAllExpected()]);
});
it('should include a reason if provided', async () => {
const { client, http, hsUrl } = createTestClient();

const roomId = "!testing:example.org";
const reason = "I am done testing here";

// noinspection TypeScriptValidateJSTypes
http.when("POST", "/_matrix/client/v3/rooms").respond(200, (path, content) => {
expect(content).toEqual({ reason });
expect(path).toEqual(`${hsUrl}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/leave`);
return {};
});

await Promise.all([client.leaveRoom(roomId, reason), http.flushAllExpected()]);
});
});

describe('forgetRoom', () => {
Expand Down
Loading