mirror of
https://github.com/Skylar-Tech/node-red-contrib-matrix-chat.git
synced 2025-04-20 04:53:06 -06:00
234 lines
11 KiB
JavaScript
234 lines
11 KiB
JavaScript
const {Phase} = require("matrix-js-sdk/lib/crypto/verification/request/VerificationRequest");
|
|
const {CryptoEvent} = require("matrix-js-sdk/lib/crypto");
|
|
|
|
module.exports = function(RED) {
|
|
const verificationRequests = new Map();
|
|
|
|
function MatrixDeviceVerification(n) {
|
|
RED.nodes.createNode(this, n);
|
|
|
|
var node = this;
|
|
|
|
this.name = n.name;
|
|
this.server = RED.nodes.getNode(n.server);
|
|
this.mode = n.mode;
|
|
|
|
if (!node.server) {
|
|
node.warn("No configuration node");
|
|
return;
|
|
}
|
|
|
|
if(!node.server.e2ee) {
|
|
node.error("End-to-end encryption needs to be enabled to use this.");
|
|
}
|
|
|
|
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
|
|
|
node.server.on("disconnected", function(){
|
|
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
|
});
|
|
|
|
node.server.on("connected", function() {
|
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
|
});
|
|
|
|
function getKeyByValue(object, value) {
|
|
return Object.keys(object).find(key => object[key] === value);
|
|
}
|
|
|
|
switch(node.mode) {
|
|
default:
|
|
node.error("Node not configured with a mode");
|
|
break;
|
|
|
|
case 'request':
|
|
node.on('input', async function(msg){
|
|
if(!msg.userId) {
|
|
node.error("msg.userId is required for start verification mode");
|
|
}
|
|
|
|
node.server.matrixClient.requestVerification(msg.userId, msg.devices || null)
|
|
.then(function(e) {
|
|
node.log("Successfully requested verification");
|
|
let verifyRequestId = msg.userId + ':' + e.channel.deviceId;
|
|
verificationRequests.set(verifyRequestId, e);
|
|
node.send({
|
|
verifyRequestId: verifyRequestId, // internally used to reference between nodes
|
|
verifyMethods: e.methods,
|
|
userId: msg.userId,
|
|
deviceIds: e.channel.devices,
|
|
selfVerification: e.isSelfVerification,
|
|
phase: getKeyByValue(Phase, e.phase)
|
|
});
|
|
})
|
|
.catch(function(e){
|
|
node.warn("Error requesting device verification: " + e);
|
|
msg.error = e;
|
|
node.send([null, msg]);
|
|
});
|
|
});
|
|
break;
|
|
|
|
case 'receive':
|
|
/**
|
|
* Fires when a key verification is requested.
|
|
* @event module:client~MatrixClient#"crypto.verification.request"
|
|
* @param {object} data
|
|
* @param {MatrixEvent} data.event the original verification request message
|
|
* @param {Array} data.methods the verification methods that can be used
|
|
* @param {Number} data.timeout the amount of milliseconds that should be waited
|
|
* before cancelling the request automatically.
|
|
* @param {Function} data.beginKeyVerification a function to call if a key
|
|
* verification should be performed. The function takes one argument: the
|
|
* name of the key verification method (taken from data.methods) to use.
|
|
* @param {Function} data.cancel a function to call if the key verification is
|
|
* rejected.
|
|
*/
|
|
node.server.matrixClient.on(CryptoEvent.VerificationRequest, async function(data){
|
|
if(data.phase === Phase.Cancelled || data.phase === Phase.Done) {
|
|
return;
|
|
}
|
|
|
|
if(data.requested || true) {
|
|
let verifyRequestId = data.targetDevice.userId + ':' + data.targetDevice.deviceId;
|
|
verificationRequests.set(verifyRequestId, data);
|
|
node.send({
|
|
verifyRequestId: verifyRequestId, // internally used to reference between nodes
|
|
verifyMethods: data.methods,
|
|
userId: data.targetDevice.userId,
|
|
deviceId: data.targetDevice.deviceId,
|
|
selfVerification: data.isSelfVerification,
|
|
phase: getKeyByValue(Phase, data.phase)
|
|
});
|
|
}
|
|
});
|
|
|
|
node.on('close', function(done) {
|
|
// clear verification requests
|
|
verificationRequests.clear();
|
|
done();
|
|
});
|
|
break;
|
|
|
|
case 'start':
|
|
node.on('input', async function(msg){
|
|
if(!msg.verifyRequestId || !verificationRequests.has(msg.verifyRequestId)) {
|
|
// if(msg.userId && msg.deviceId) {
|
|
// node.server.beginKeyVerification("m.sas.v1", msg.userId, msg.deviceId);
|
|
// }
|
|
|
|
node.error("invalid verification request (invalid msg.verifyRequestId): " + (msg.verifyRequestId || null));
|
|
}
|
|
|
|
var data = verificationRequests.get(msg.verifyRequestId);
|
|
if(msg.cancel) {
|
|
await data._verifier.cancel();
|
|
verificationRequests.delete(msg.verifyRequestId);
|
|
} else {
|
|
try {
|
|
data.on('change', async function() {
|
|
var that = this;
|
|
if(this.phase === Phase.Started) {
|
|
let verifierCancel = function(){
|
|
let verifyRequestId = that.targetDevice.userId + ':' + that.targetDevice.deviceId;
|
|
if(verificationRequests.has(verifyRequestId)) {
|
|
verificationRequests.delete(verifyRequestId);
|
|
}
|
|
};
|
|
|
|
data._verifier.on('cancel', function(e){
|
|
node.warn("Device verification cancelled " + e);
|
|
verifierCancel();
|
|
});
|
|
|
|
let show_sas = function(e) {
|
|
// e = {
|
|
// sas: {
|
|
// decimal: [ 8641, 3153, 2357 ],
|
|
// emoji: [
|
|
// [Array], [Array],
|
|
// [Array], [Array],
|
|
// [Array], [Array],
|
|
// [Array]
|
|
// ]
|
|
// },
|
|
// confirm: [AsyncFunction: confirm],
|
|
// cancel: [Function: cancel],
|
|
// mismatch: [Function: mismatch]
|
|
// }
|
|
msg.payload = e.sas;
|
|
msg.emojis = e.sas.emoji.map(function(emoji, i) {
|
|
return emoji[0];
|
|
});
|
|
msg.emojis_text = e.sas.emoji.map(function(emoji, i) {
|
|
return emoji[1];
|
|
});
|
|
node.send(msg);
|
|
};
|
|
data._verifier.on('show_sas', show_sas);
|
|
data._verifier.verify()
|
|
.then(function(e){
|
|
data._verifier.off('show_sas', show_sas);
|
|
data._verifier.done();
|
|
}, function(e) {
|
|
verifierCancel();
|
|
node.warn(e);
|
|
// @todo return over second output
|
|
});
|
|
}
|
|
});
|
|
|
|
data.emit("change");
|
|
await data.accept();
|
|
} catch(e) {
|
|
console.log("ERROR", e);
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'cancel':
|
|
node.on('input', async function(msg){
|
|
if(!msg.verifyRequestId || !verificationRequests.has(msg.verifyRequestId)) {
|
|
node.error("Invalid verification request: " + (msg.verifyRequestId || null));
|
|
}
|
|
|
|
var data = verificationRequests.get(msg.verifyRequestId);
|
|
if(data) {
|
|
data.cancel()
|
|
.then(function(e){
|
|
node.send([msg, null]);
|
|
})
|
|
.catch(function(e) {
|
|
msg.error = e;
|
|
node.send([null, msg]);
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'accept':
|
|
node.on('input', async function(msg){
|
|
if(!msg.verifyRequestId || !verificationRequests.has(msg.verifyRequestId)) {
|
|
node.error("Invalid verification request: " + (msg.verifyRequestId || null));
|
|
}
|
|
|
|
var data = verificationRequests.get(msg.verifyRequestId);
|
|
if(data._verifier && data._verifier.sasEvent) {
|
|
data._verifier.sasEvent.confirm()
|
|
.then(function(e){
|
|
node.send([msg, null]);
|
|
})
|
|
.catch(function(e) {
|
|
msg.error = e;
|
|
node.send([null, msg]);
|
|
});
|
|
} else {
|
|
node.error("Verification must be started");
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
RED.nodes.registerType("matrix-device-verification", MatrixDeviceVerification);
|
|
} |