parent
ed146e98d8
commit
d01733c647
@ -27,6 +27,7 @@
|
||||
"matrix-crypt-file": "src/matrix-crypt-file.js",
|
||||
"matrix-room-kick": "src/matrix-room-kick.js",
|
||||
"matrix-room-ban": "src/matrix-room-ban.js",
|
||||
"matrix-device-verification": "src/matrix-device-verification.js",
|
||||
"matrix-synapse-users": "src/matrix-synapse-users.js",
|
||||
"matrix-synapse-register": "src/matrix-synapse-register.js",
|
||||
"matrix-synapse-create-edit-user": "src/matrix-synapse-create-edit-user.js",
|
||||
|
232
src/matrix-device-verification.html
Normal file
232
src/matrix-device-verification.html
Normal file
@ -0,0 +1,232 @@
|
||||
<script type="text/javascript">
|
||||
let computeInputAndOutputCounts = function(node){
|
||||
switch($("#node-input-mode").val()) {
|
||||
default:
|
||||
node.outputs = node.inputs = 0;
|
||||
break;
|
||||
case 'receive':
|
||||
node.outputs = 1;
|
||||
node.inputs = 0;
|
||||
break;
|
||||
case 'request':
|
||||
case 'start':
|
||||
case 'accept':
|
||||
case 'cancel':
|
||||
node.outputs = 2;
|
||||
node.inputs = 1;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
RED.nodes.registerType('matrix-device-verification', {
|
||||
category: 'matrix',
|
||||
color: '#00b7ca',
|
||||
icon: "matrix.png",
|
||||
inputs: 0,
|
||||
outputs: 0,
|
||||
outputLabels: ["success", "error"],
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
mode: { value: null, type: "text", required: true },
|
||||
inputs: { value: 0 },
|
||||
outputs: { value: 0 }
|
||||
},
|
||||
oneditprepare: function () {
|
||||
computeInputAndOutputCounts(this);
|
||||
},
|
||||
oneditsave: function () {
|
||||
computeInputAndOutputCounts(this);
|
||||
},
|
||||
label: function() {
|
||||
if(this.name) {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
switch(this.mode) {
|
||||
default:
|
||||
return 'Device Verification';
|
||||
case 'receive':
|
||||
return 'Receive Device Verification';
|
||||
case 'request':
|
||||
return 'Request Device Verification';
|
||||
case 'start':
|
||||
return 'Start Device Verification';
|
||||
case 'accept':
|
||||
return 'Accept Device Verification';
|
||||
case 'cancel':
|
||||
return 'Cancel Device Verification';
|
||||
}
|
||||
return this.name || "Device Verify Request";
|
||||
},
|
||||
paletteLabel: function(){
|
||||
return "Device Verification";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="matrix-device-verification">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="fa fa-user"></i> Matrix Server Config</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-mode"><i class="fa fa-user"></i> Mode</label>
|
||||
<select id="node-input-mode" style="width:70%;">
|
||||
<option value="">Unconfigured</option>
|
||||
<option value="receive">Receive Verification Request</option>
|
||||
<option value="request">Request Verification</option>
|
||||
<option value="start">Verification Start</option>
|
||||
<option value="accept">Verification Accept</option>
|
||||
<option value="cancel">Verification Cancel</option>
|
||||
</select>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="matrix-device-verification">
|
||||
<h3>Details</h3>
|
||||
<p>
|
||||
Handle device verification.
|
||||
<br /><br />
|
||||
THIS NODE IS IN BETA. There is a good chance that we will change how this node works later down the road. Make sure to read the release notes before upgrading.
|
||||
</p>
|
||||
<a href="https://matrix-org.github.io/synapse/develop/admin_api/room_membership.html#edit-room-membership-api" target="_blank">Synapse API Endpoint Information</a>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<ul class="node-inputs">
|
||||
<li><code>mode</code> set to '<strong>Receive Verification Request</strong>'
|
||||
<div class="form-tips" style="margin-bottom: 12px;">
|
||||
Doesn't take an input
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Request Verification</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.userId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
ID of the user to request device verification from
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.devices <span class="property-type">array[string]|null</span></dt>
|
||||
<dd> list of <code>msg.userId</code>'s devices IDs to request verification from. If empty it will request from all known devices.</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Verification Start</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.verifyRequestId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
Internal ID to reference the verification request throughout the flows
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.cancel <span class="property-type">bool</span></dt>
|
||||
<dd>
|
||||
If set and is true the verification request will be cancelled
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Verification Accept</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.verifyRequestId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
Internal ID to reference the verification request throughout the flows
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Verification Cancel</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.verifyRequestId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
Internal ID to reference the verification request throughout the flows
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<ul class="node-outputs">
|
||||
<li><code>mode</code> set to '<strong>Receive Verification Request</strong>' or '<strong>Request Verification</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.verifyRequestId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
Internal ID to reference the verification request throughout the flows
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.verifyMethods <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
Common verification methods supported by both sides
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.userId <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
ID of the user to request device verification from
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.deviceIds <span class="property-type">array[string]</span></dt>
|
||||
<dd>
|
||||
List of devices we are verifying
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.selfVerification <span class="property-type">bool</span></dt>
|
||||
<dd>
|
||||
true if we are verifying one of our own devices
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.phase <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
what phase of verification we are in
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Verification Start</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.payload <span class="property-type">string</span></dt>
|
||||
<dd>
|
||||
sas verification payload
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.emojis <span class="property-type">array[string]</span></dt>
|
||||
<dd>
|
||||
array of emojis for verification request
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.emojis_text <span class="property-type">array[string]</span></dt>
|
||||
<dd>
|
||||
array of emojis in text form for verification request
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>mode</code> set to '<strong>Verification Accept</strong>' or '<strong>Verification Cancel</strong>'
|
||||
<div class="form-tips" style="margin-bottom: 12px;">
|
||||
Passes input straight to output on success. If an error occurs it goes to the second output.
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</script>
|
234
src/matrix-device-verification.js
Normal file
234
src/matrix-device-verification.js
Normal file
@ -0,0 +1,234 @@
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user