-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Implement lab for encrypted state events (MSC3414/MSC4362) #30877
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
base: develop
Are you sure you want to change the base?
Changes from 21 commits
8c73c33
7d8f991
d86bea5
e7104b7
03d43ce
8a08134
d51ba4b
d053a20
f568949
a2ff003
7a9d462
6b3f273
df86a11
d7092fb
da30063
b691621
ebd42c1
bed2888
fe4c48f
d11cb24
0808466
0fc92d4
bb874c4
d538135
10f34e1
330ec69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,8 +20,12 @@ import { | |
Preset, | ||
RestrictedAllowType, | ||
Visibility, | ||
Direction, | ||
RoomStateEvent, | ||
type RoomState, | ||
} from "matrix-js-sdk/src/matrix"; | ||
import { logger } from "matrix-js-sdk/src/logger"; | ||
import { type RoomEncryptionEventContent } from "matrix-js-sdk/src/types"; | ||
|
||
import Modal, { type IHandle } from "./Modal"; | ||
import { _t, UserFriendlyError } from "./languageHandler"; | ||
|
@@ -53,6 +57,7 @@ export interface IOpts { | |
spinner?: boolean; | ||
guestAccess?: boolean; | ||
encryption?: boolean; | ||
stateEncryption?: boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new public field needs doc-comment, please |
||
inlineErrors?: boolean; | ||
andView?: boolean; | ||
avatar?: File | string; // will upload if given file, else mxcUrl is needed | ||
|
@@ -100,6 +105,7 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro | |
if (opts.spinner === undefined) opts.spinner = true; | ||
if (opts.guestAccess === undefined) opts.guestAccess = true; | ||
if (opts.encryption === undefined) opts.encryption = false; | ||
if (opts.stateEncryption === undefined) opts.stateEncryption = false; | ||
|
||
if (client.isGuest()) { | ||
dis.dispatch({ action: "require_registration" }); | ||
|
@@ -208,13 +214,21 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro | |
}); | ||
} | ||
|
||
let stateEncryptedOpts: ICreateRoomOpts | undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. having a separate copy of the create opts feels like a confusing way to do things. I think really, it's incorrect to pass the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pulled it out here: #30981 |
||
if (opts.encryption) { | ||
const content: RoomEncryptionEventContent = { | ||
algorithm: MEGOLM_ENCRYPTION_ALGORITHM, | ||
}; | ||
if (opts.stateEncryption) { | ||
content["io.element.msc3414.encrypt_state_events"] = true; | ||
// Erase room name, since we want to encrypt it. Copy options for later use. | ||
stateEncryptedOpts = { ...createOpts }; | ||
delete createOpts.name; | ||
} | ||
createOpts.initial_state.push({ | ||
type: "m.room.encryption", | ||
state_key: "", | ||
content: { | ||
algorithm: MEGOLM_ENCRYPTION_ALGORITHM, | ||
}, | ||
content, | ||
}); | ||
} | ||
|
||
|
@@ -251,7 +265,7 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro | |
}); | ||
} | ||
|
||
if (opts.avatar) { | ||
if (opts.avatar && !opts.stateEncryption) { | ||
let url = opts.avatar; | ||
if (opts.avatar instanceof File) { | ||
({ content_uri: url } = await client.uploadContent(opts.avatar)); | ||
|
@@ -320,6 +334,54 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro | |
|
||
if (opts.dmUserId) await Rooms.setDMRoom(client, roomId, opts.dmUserId); | ||
}) | ||
.then(async () => { | ||
// We need to set up initial state manually if state encryption is enabled, since it needs | ||
// to be encrypted. | ||
if (opts.stateEncryption && stateEncryptedOpts) { | ||
const resolvedRoom = await room; | ||
|
||
await new Promise<void>((resolve, reject) => { | ||
if (resolvedRoom.hasEncryptionStateEvent()) { | ||
return resolve(); | ||
} | ||
|
||
const roomState = resolvedRoom.getLiveTimeline().getState(Direction.Forward)!; | ||
|
||
// Soft fail, since the room will still be functional if the initial state is not encrypted. | ||
|
||
const timeout = setTimeout(() => { | ||
logger.warn("Timed out while waiting for room to enable encryption"); | ||
roomState.off(RoomStateEvent.Update, onRoomStateUpdate); | ||
resolve(); | ||
}, 3000); | ||
|
||
|
||
const onRoomStateUpdate = (state: RoomState): void => { | ||
if (state.getStateEvents(EventType.RoomEncryption, "")) { | ||
roomState.off(RoomStateEvent.Update, onRoomStateUpdate); | ||
clearTimeout(timeout); | ||
resolve(); | ||
} | ||
}; | ||
|
||
roomState.on(RoomStateEvent.Update, onRoomStateUpdate); | ||
}); | ||
|
||
|
||
// Set room name | ||
if (stateEncryptedOpts.name) { | ||
await client.setRoomName(roomId, stateEncryptedOpts.name); | ||
} | ||
|
||
// Set room avatar | ||
if (opts.avatar) { | ||
let url: string; | ||
if (opts.avatar instanceof File) { | ||
({ content_uri: url } = await client.uploadContent(opts.avatar)); | ||
} else { | ||
url = opts.avatar; | ||
} | ||
await client.sendStateEvent(roomId, EventType.RoomAvatar, { url }, ""); | ||
} | ||
} | ||
|
||
}) | ||
.then(() => { | ||
if (opts.parentSpace) { | ||
return SpaceStore.instance.addRoomToSpace( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
Copyright 2024 New Vector Ltd. | ||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial | ||
Please see LICENSE files in the repository root for full details. | ||
*/ | ||
|
||
import PlatformPeg from "../../PlatformPeg"; | ||
import { SettingLevel } from "../SettingLevel"; | ||
import SettingsStore from "../SettingsStore"; | ||
import SettingController from "./SettingController"; | ||
|
||
export default class EncryptedStateEventsController extends SettingController { | ||
public onChange(): void { | ||
SettingsStore.setValue("feature_share_history_on_invite", null, SettingLevel.CONFIG, true); | ||
kaylendog marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
PlatformPeg.get()?.reload(); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.