Implement own device removal via stanza

This commit is contained in:
JC Brand 2018-08-21 16:19:57 +02:00
parent 26e936583f
commit a06d2c494a
5 changed files with 123 additions and 68 deletions

View File

@ -8575,8 +8575,10 @@ body.reset {
margin-top: 1em; }
#conversejs #converse-modals .btn {
font-weight: normal; }
#conversejs #converse-modals #user-profile-modal label {
#conversejs #converse-modals #user-profile-modal .profile-form label {
font-weight: bold; }
#conversejs #converse-modals #user-profile-modal .fingerprint-removal label {
padding: 0.75rem 1.25rem; }
#conversejs #converse-modals #user-profile-modal .list-group-item {
display: flex;
justify-content: left;

View File

@ -16,9 +16,17 @@
}
#user-profile-modal {
label {
font-weight: bold;
.profile-form {
label {
font-weight: bold;
}
}
.fingerprint-removal {
label {
padding: 0.75rem 1.25rem;
}
}
.list-group-item {
display: flex;
justify-content: left;

View File

@ -259,7 +259,7 @@
});
this.messages = new _converse.Messages();
this.messages.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`));
`converse.messages${this.get('jid')}${_converse.bare_jid}`);
this.messages.chatbox = this;
this.messages.on('change:upload', (message) => {
@ -626,7 +626,7 @@
onConnected () {
this.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`));
`converse.chatboxes-${_converse.bare_jid}`);
this.registerMessageHandler();
this.fetch({
'add': true,

View File

@ -77,21 +77,25 @@
},
initialize () {
const { _converse } = this.__super__,
device_id = _converse.omemo_store.get('device_id');
const { _converse } = this.__super__;
this.debouncedRender = _.debounce(this.render, 50);
this.devicelist = _converse.devicelists.get(_converse.bare_jid);
this.current_device = this.devicelist.devices.get(device_id);
this.other_devices = this.devicelist.devices.filter(d => (d.get('id') !== device_id));
this.devicelist.devices.on('change:bundle', this.render, this);
this.devicelist.devices.on('change:bundle', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
return this.__super__.initialize.apply(this, arguments);
},
beforeRender () {
const { _converse } = this.__super__,
device_id = _converse.omemo_store.get('device_id').toString();
this.current_device = this.devicelist.devices.get(device_id);
this.other_devices = this.devicelist.devices.filter(d => (d.get('id') !== device_id));
},
selectAll (ev) {
let sibling = ev.target.parentElement.nextElementSibling;
let sibling = u.ancestor(ev.target, 'li');
while (sibling) {
sibling.firstElementChild.checked = ev.target.checked;
sibling.querySelector('input[type="checkbox"]').checked = ev.target.checked;
sibling = sibling.nextElementSibling;
}
},
@ -99,9 +103,20 @@
removeSelectedFingerprints (ev) {
ev.preventDefault();
ev.stopPropagation();
ev.target.querySelector('.select-all').checked = false
const checkboxes = ev.target.querySelectorAll('.fingerprint-removal-item input[type="checkbox"]:checked'),
device_ids = _.map(checkboxes, 'value');
this.devicelist.removeOwnDevices(device_ids);
this.devicelist.removeOwnDevices(device_ids)
.then(this.modal.hide)
.catch(err => {
const { _converse } = this.__super__,
{ __ } = _converse;
_converse.log(err, Strophe.LogLevel.ERROR);
_converse.api.alert.show(
Strophe.LogLevel.ERROR,
__('Error'), [__('Sorry, an error occurred while trying to remove the devices.')]
)
});
},
},
@ -628,21 +643,27 @@
return Promise.resolve();
},
createNewDeviceBundle () {
return generateBundle().then((data) => {
// TODO: should storeSession be used here?
_converse.omemo_store.save(data);
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
},
fetchSession () {
if (_.isUndefined(this._setup_promise)) {
this._setup_promise = new Promise((resolve, reject) => {
this.fetch({
'success': () => {
if (!_converse.omemo_store.get('device_id')) {
generateBundle()
.then((data) => {
// TODO: should storeSession be used here?
_converse.omemo_store.save(data);
resolve();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
this.createNewDeviceBundle().then(resolve).catch(resolve);
} else {
resolve();
}
},
'error': () => {
this.createNewDeviceBundle().then(resolve).catch(resolve);
}
});
});
@ -704,9 +725,9 @@
initialize () {
this.devices = new _converse.Devices();
this.devices.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`)
);
const id = `converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`;
this.devices.id = id;
this.devices.browserStorage = new Backbone.BrowserStorage.session(id);
this.fetchDevices();
},
@ -720,6 +741,9 @@
} else {
resolve();
}
},
'error': () => {
this.fetchDevicesFromServer().then(resolve).catch(reject);
}
});
});
@ -749,30 +773,39 @@
});
},
addDeviceToList (device_id) {
publishDevices () {
const stanza = $iq({
'from': _converse.bare_jid,
'type': 'set'
}).c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('publish', {'node': Strophe.NS.OMEMO_DEVICELIST})
.c('item')
.c('list', {'xmlns': Strophe.NS.OMEMO})
_.each(this.devices.where({'active': true}), (device) => {
stanza.c('device', {'id': device.get('id')}).up();
});
return _converse.api.sendIQ(stanza);
},
addOwnDevice (device_id) {
/* Add this device to our list of devices stored on the
* server.
* https://xmpp.org/extensions/xep-0384.html#usecases-announcing
*/
this.devices.create({'id': device_id, 'jid': this.get('jid')});
return new Promise((resolve, reject) => {
const stanza = $iq({
'from': _converse.bare_jid,
'type': 'set'
}).c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('publish', {'node': Strophe.NS.OMEMO_DEVICELIST})
.c('item')
.c('list', {'xmlns': Strophe.NS.OMEMO})
_.each(this.devices.where({'active': true}), (device) => {
stanza.c('device', {'id': device.get('id')}).up();
});
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT);
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot add device to someone else's device list");
}
this.devices.create({'id': device_id.toString(), 'jid': this.get('jid')});
return this.publishDevices();
},
removeOwnDevices (device_ids) {
// TODO
if (this.get('jid') !== _converse.bare_jid) {
throw new Error("Cannot remove devices from someone else's device list");
}
this.devices.reset(this.devices.filter(d => (!_.includes(device_ids, d.get('id').toString()))));
return this.publishDevices();
}
});
@ -814,18 +847,19 @@
}
function fetchDeviceLists () {
return new Promise((resolve, reject) => _converse.devicelists.fetch({'success': resolve}));
return new Promise((resolve, reject) => _converse.devicelists.fetch({
'success': resolve,
'error': resolve
}));
}
function fetchOwnDevices () {
return new Promise((resolve, reject) => {
fetchDeviceLists().then(() => {
let own_devicelist = _converse.devicelists.get(_converse.bare_jid);
if (_.isNil(own_devicelist)) {
own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid});
}
own_devicelist.fetchDevices().then(resolve).catch(reject);
});
return fetchDeviceLists().then(() => {
let own_devicelist = _converse.devicelists.get(_converse.bare_jid);
if (_.isNil(own_devicelist)) {
own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid});
}
return own_devicelist.fetchDevices();
});
}
@ -834,14 +868,14 @@
* Also, deduplicate devices if necessary.
*/
const devicelist = _converse.devicelists.get(_converse.bare_jid),
device_id = _converse.omemo_store.get('device_id'),
device_id = _converse.omemo_store.get('device_id').toString(),
own_device = devicelist.devices.findWhere({'id': device_id});
if (!own_device) {
return devicelist.addDeviceToList(device_id);
return devicelist.addOwnDevice(device_id);
} else if (!own_device.get('active')) {
own_device.set('active', true, {'silent': true});
return devicelist.addDeviceToList(device_id);
return devicelist.addOwnDevice(device_id);
} else {
return Promise.resolve();
}
@ -903,18 +937,19 @@
function restoreOMEMOSession () {
if (_.isUndefined(_converse.omemo_store)) {
_converse.omemo_store = new _converse.OMEMOStore();
_converse.omemo_store.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.omemosession-${_converse.bare_jid}`)
);
const id = b64_sha1(`converse.omemosession-${_converse.bare_jid}`);
_converse.omemo_store.id = id;
_converse.omemo_store.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
}
return _converse.omemo_store.fetchSession();
}
function initOMEMO() {
_converse.devicelists = new _converse.DeviceLists();
_converse.devicelists.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.devicelists-${_converse.bare_jid}`)
);
const id = `converse.devicelists-${_converse.bare_jid}`;
_converse.devicelists.id = id;
_converse.devicelists.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
fetchOwnDevices()
.then(() => restoreOMEMOSession())
.then(() => updateOwnDeviceList())

View File

@ -68,37 +68,47 @@
<form class="converse-form fingerprint-removal">
<ul class="list-group fingerprints">
<li class="list-group-item active">{{{o.__("This device's OMEMO fingerprint")}}}</li>
<li class="fingerprint-removal-item list-group-item">
<li class="list-group-item">
{[ if (o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ]}
<input type="checkbox" value="{{{o.view.current_device.get('id')}}}"
aria-label="{{{o.__('Checkbox for removing the following fingerprint')}}}">
<span class="fingerprint">{{{o.view.current_device.get('bundle').fingerprint}}}</span>
{[ } else {]}
<span class="spinner fa fa-spinner centered"/>
{[ } ]}
</li>
</ul>
{[ if (o.view.other_devices) { ]}
{[ if (o.view.other_devices.length) { ]}
<ul class="list-group fingerprints">
<li class="list-group-item active">
<li class="list-group-item nopadding active">
<label>
<input type="checkbox" class="select-all" title="{{{o.__('Select all')}}}"
aria-label="{{{o.__('Checkbox to select fingerprints of all other OMEMO devices')}}}">
{{{o.__('Other OMEMO-enabled devices')}}}
</label>
</li>
{[ o._.forEach(o.view.other_devices, function (device) { ]}
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
<li class="fingerprint-removal-item list-group-item">
<li class="fingerprint-removal-item list-group-item nopadding">
<label>
<input type="checkbox" value="{{{device.get('id')}}}"
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
<span class="fingerprint">{{{device.get('bundle').fingerprint}}}</span>
</label>
</li>
{[ } else {]}
<li class="fingerprint-removal-item list-group-item nopadding">
<label>
<input type="checkbox" value="{{{device.get('id')}}}"
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
<span>{{{o.__('Device without a fingerprint')}}}</span>
</label>
</li>
{[ } ]}
{[ }); ]}
</ul>
<div class="form-group">
<button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button>
</div>
{[ } ]}
<div class="form-group">
<button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button>
</div>
</form>
</div>
{[ } ]}