Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Support MSC3086 asserted identity #5886

Merged
merged 7 commits into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 60 additions & 7 deletions src/CallHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import {MatrixClientPeg} from './MatrixClientPeg';
import PlatformPeg from './PlatformPeg';
import Modal from './Modal';
import { _t } from './languageHandler';
import { createNewMatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import dis from './dispatcher/dispatcher';
import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore';
Expand All @@ -86,6 +85,8 @@ import { Action } from './dispatcher/actions';
import VoipUserMapper from './VoipUserMapper';
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
import { randomUppercaseString, randomLowercaseString } from "matrix-js-sdk/src/randomstring";
import SdkConfig from './SdkConfig';
import { ensureDMExists, findDMForUser } from './createRoom';

export const PROTOCOL_PSTN = 'm.protocol.pstn';
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
Expand Down Expand Up @@ -167,6 +168,11 @@ export default class CallHandler {
private invitedRoomsAreVirtual = new Map<string, boolean>();
private invitedRoomCheckInProgress = false;

// Map of the asserted identity users after we've looked them up using the API.
// We need to be be able to determine the mapped room synchronously, so we
// do the async lookup when we get new information and then store these mappings here
private assertedIdentityNativeUsers = new Map<string, string>();

static sharedInstance() {
if (!window.mxCallHandler) {
window.mxCallHandler = new CallHandler()
Expand All @@ -179,8 +185,19 @@ export default class CallHandler {
* Gets the user-facing room associated with a call (call.roomId may be the call "virtual room"
* if a voip_mxid_translate_pattern is set in the config)
*/
public static roomIdForCall(call: MatrixCall): string {
public roomIdForCall(call: MatrixCall): string {
if (!call) return null;

const voipConfig = SdkConfig.get()['voip'];

if (voipConfig && voipConfig.obeyAssertedIdentity) {
const nativeUser = this.assertedIdentityNativeUsers[call.callId];
if (nativeUser) {
const room = findDMForUser(MatrixClientPeg.get(), nativeUser);
if (room) return room.roomId
}
}

return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) || call.roomId;
}

Expand Down Expand Up @@ -379,14 +396,14 @@ export default class CallHandler {
// We don't allow placing more than one call per room, but that doesn't mean there
// can't be more than one, eg. in a glare situation. This checks that the given call
// is the call we consider 'the' call for its room.
const mappedRoomId = CallHandler.roomIdForCall(call);
const mappedRoomId = this.roomIdForCall(call);

const callForThisRoom = this.getCallForRoom(mappedRoomId);
return callForThisRoom && call.callId === callForThisRoom.callId;
}

private setCallListeners(call: MatrixCall) {
const mappedRoomId = CallHandler.roomIdForCall(call);
let mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call);

call.on(CallEvent.Error, (err: CallError) => {
if (!this.matchesCallForThisRoom(call)) return;
Expand Down Expand Up @@ -500,6 +517,42 @@ export default class CallHandler {
this.setCallListeners(newCall);
this.setCallState(newCall, newCall.state);
});
call.on(CallEvent.AssertedIdentityChanged, async () => {
if (!this.matchesCallForThisRoom(call)) return;

console.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity());

const newAssertedIdentity = call.getRemoteAssertedIdentity().id;
let newNativeAssertedIdentity = newAssertedIdentity;
if (newAssertedIdentity) {
const response = await this.sipNativeLookup(newAssertedIdentity);
if (response.length) newNativeAssertedIdentity = response[0].userid;
}
console.log(`Asserted identity ${newAssertedIdentity} mapped to ${newNativeAssertedIdentity}`);

if (newNativeAssertedIdentity) {
this.assertedIdentityNativeUsers[call.callId] = newNativeAssertedIdentity;

// If we don't already have a room with this user, make one. This will be slightly odd
// if they called us because we'll be inviting them, but there's not much we can do about
// this if we want the actual, native room to exist (which we do). This is why it's
// important to only obey asserted identity in trusted environments, since anyone you're
// on a call with can cause you to send a room invite to someone.
await ensureDMExists(MatrixClientPeg.get(), newNativeAssertedIdentity);

const newMappedRoomId = this.roomIdForCall(call);
console.log(`Old room ID: ${mappedRoomId}, new room ID: ${newMappedRoomId}`);
if (newMappedRoomId !== mappedRoomId) {
this.removeCallForRoom(mappedRoomId);
mappedRoomId = newMappedRoomId;
this.calls.set(mappedRoomId, call);
dis.dispatch({
action: Action.CallChangeRoom,
call,
});
}
}
});
}

private async logCallStats(call: MatrixCall, mappedRoomId: string) {
Expand Down Expand Up @@ -551,7 +604,7 @@ export default class CallHandler {
}

private setCallState(call: MatrixCall, status: CallState) {
const mappedRoomId = CallHandler.roomIdForCall(call);
const mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call);

console.log(
`Call state in ${mappedRoomId} changed to ${status}`,
Expand Down Expand Up @@ -639,7 +692,7 @@ export default class CallHandler {

const timeUntilTurnCresExpire = MatrixClientPeg.get().getTurnServersExpiry() - Date.now();
console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId);
const call = MatrixClientPeg.get().createCall(mappedRoomId);

this.calls.set(roomId, call);
if (transferee) {
Expand Down Expand Up @@ -772,7 +825,7 @@ export default class CallHandler {

const call = payload.call as MatrixCall;

const mappedRoomId = CallHandler.roomIdForCall(call);
const mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call);
if (this.getCallForRoom(mappedRoomId)) {
// ignore multiple incoming calls to the same room
return;
Expand Down
6 changes: 5 additions & 1 deletion src/VoipUserMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ export default class VoipUserMapper {
if (!virtualRoom) return null;
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null;
return virtualRoomEvent.getContent()['native_room'] || null;
const nativeRoomID = virtualRoomEvent.getContent()['native_room'];
const nativeRoom = MatrixClientPeg.get().getRoom(nativeRoomID);
if (!nativeRoom || nativeRoom.getMyMembership() !== 'join') return null;

return nativeRoomID;
}

public isVirtualRoom(room: Room): boolean {
Expand Down
2 changes: 2 additions & 0 deletions src/components/views/voip/CallPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { CallEvent, CallState, MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { Action } from '../../../dispatcher/actions';

const SHOW_CALL_IN_STATES = [
CallState.Connected,
Expand Down Expand Up @@ -142,6 +143,7 @@ export default class CallPreview extends React.Component<IProps, IState> {
switch (payload.action) {
// listen for call state changes to prod the render method, which
// may hide the global CallView if the call it is tracking is dead
case Action.CallChangeRoom:
case 'call_state': {
const [primaryCall, secondaryCalls] = getPrimarySecondaryCalls(
CallHandler.sharedInstance().getAllActiveCallsNotInRoom(this.state.roomId),
Expand Down
18 changes: 10 additions & 8 deletions src/components/views/voip/CallView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export default class CallView extends React.Component<IProps, IState> {
};

private onExpandClick = () => {
const userFacingRoomId = CallHandler.roomIdForCall(this.props.call);
const userFacingRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
dis.dispatch({
action: 'view_room',
room_id: userFacingRoomId,
Expand Down Expand Up @@ -337,15 +337,15 @@ export default class CallView extends React.Component<IProps, IState> {
};

private onRoomAvatarClick = () => {
const userFacingRoomId = CallHandler.roomIdForCall(this.props.call);
const userFacingRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
dis.dispatch({
action: 'view_room',
room_id: userFacingRoomId,
});
}

private onSecondaryRoomAvatarClick = () => {
const userFacingRoomId = CallHandler.roomIdForCall(this.props.secondaryCall);
const userFacingRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.secondaryCall);

dis.dispatch({
action: 'view_room',
Expand All @@ -354,7 +354,7 @@ export default class CallView extends React.Component<IProps, IState> {
}

private onCallResumeClick = () => {
const userFacingRoomId = CallHandler.roomIdForCall(this.props.call);
const userFacingRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
CallHandler.sharedInstance().setActiveCallRoomId(userFacingRoomId);
}

Expand All @@ -365,8 +365,8 @@ export default class CallView extends React.Component<IProps, IState> {

public render() {
const client = MatrixClientPeg.get();
const callRoomId = CallHandler.roomIdForCall(this.props.call);
const secondaryCallRoomId = CallHandler.roomIdForCall(this.props.secondaryCall);
const callRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
const secondaryCallRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.secondaryCall);
const callRoom = client.getRoom(callRoomId);
const secCallRoom = this.props.secondaryCall ? client.getRoom(secondaryCallRoomId) : null;

Expand Down Expand Up @@ -482,11 +482,13 @@ export default class CallView extends React.Component<IProps, IState> {
const isOnHold = this.state.isLocalOnHold || this.state.isRemoteOnHold;
let holdTransferContent;
if (transfereeCall) {
const transferTargetRoom = MatrixClientPeg.get().getRoom(CallHandler.roomIdForCall(this.props.call));
const transferTargetRoom = MatrixClientPeg.get().getRoom(
CallHandler.sharedInstance().roomIdForCall(this.props.call),
);
const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("unknown person");

const transfereeRoom = MatrixClientPeg.get().getRoom(
CallHandler.roomIdForCall(transfereeCall),
CallHandler.sharedInstance().roomIdForCall(transfereeCall),
);
const transfereeName = transfereeRoom ? transfereeRoom.name : _t("unknown person");

Expand Down
2 changes: 2 additions & 0 deletions src/components/views/voip/CallViewForRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import dis from '../../../dispatcher/dispatcher';
import {Resizable} from "re-resizable";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { Action } from '../../../dispatcher/actions';

interface IProps {
// What room we should display the call for
Expand Down Expand Up @@ -62,6 +63,7 @@ export default class CallViewForRoom extends React.Component<IProps, IState> {

private onAction = (payload) => {
switch (payload.action) {
case Action.CallChangeRoom:
case 'call_state': {
const newCall = this.getCall();
if (newCall !== this.state.call) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/voip/IncomingCallBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
e.stopPropagation();
dis.dispatch({
action: 'answer',
room_id: CallHandler.roomIdForCall(this.state.incomingCall),
room_id: CallHandler.sharedInstance().roomIdForCall(this.state.incomingCall),
});
};

private onRejectClick: React.MouseEventHandler = (e) => {
e.stopPropagation();
dis.dispatch({
action: 'reject',
room_id: CallHandler.roomIdForCall(this.state.incomingCall),
room_id: CallHandler.sharedInstance().roomIdForCall(this.state.incomingCall),
});
};

Expand All @@ -91,7 +91,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {

let room = null;
if (this.state.incomingCall) {
room = MatrixClientPeg.get().getRoom(CallHandler.roomIdForCall(this.state.incomingCall));
room = MatrixClientPeg.get().getRoom(CallHandler.sharedInstance().roomIdForCall(this.state.incomingCall));
}

const caller = room ? room.name : _t("Unknown caller");
Expand Down
3 changes: 3 additions & 0 deletions src/dispatcher/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ export enum Action {
*/
VirtualRoomSupportUpdated = "virtual_room_support_updated",

// Probably would be better to have a VoIP states in a store and have the store emit changes
CallChangeRoom = "call_change_room",

/**
* Fired when an upload has started. Should be used with UploadStartedPayload.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/utils/DMRoomMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ export default class DMRoomMap {
return DMRoomMap.sharedInstance;
}

/**
* Set the shared instance to the instance supplied
* Used by tests
* @param inst the new shared instance
*/
public static setShared(inst: DMRoomMap) {
DMRoomMap.sharedInstance = inst;
}

/**
* Returns a shared instance of the class
* that uses the singleton matrix client
Expand Down
Loading