Remove all concept of 'key conflict' from the app

This commit is contained in:
Scott Nonnenberg 2017-06-08 18:08:41 -07:00
parent 51080141cd
commit ee0b0f5ffb
15 changed files with 28 additions and 400 deletions

View file

@ -69,12 +69,6 @@
"identityChanged": {
"message": "Your safety number with this contact has changed. This could either mean that someone is trying to intercept your communication, or this contact simply reinstalled Signal. You may wish to verify the new safety number below."
},
"outgoingKeyConflict": {
"message": "Your safety number with this contact has changed. Click to process and display."
},
"incomingKeyConflict": {
"message": "Received message with a new safety number. Click to process and display."
},
"incomingError": {
"message": "Error handling incoming message."
},

View file

@ -280,12 +280,6 @@
</script>
<script type='text/x-tmpl-mustache' id='message-detail'>
<div class='container'>
{{ #hasConflict }}
<div class='hasConflict clearfix'>
<div class='conflicts'>
</div>
</div>
{{ /hasConflict }}
<div class='message-container'></div>
<div class='info'>
<table>
@ -315,13 +309,8 @@
<script type='text/x-tmpl-mustache' id='group-member-list'>
<div class='container'></div>
</script>
<script type='text/x-tmpl-mustache' id='key_verification_panel'>
<script type='text/x-tmpl-mustache' id='key-verification'>
<div class='container'>
{{> key_verification }}
<p> {{> link_to_support }} </p>
</div>
</script>
<script type='text/x-tmpl-mustache' id='key_verification'>
{{ ^has_their_key }}
<div class='placeholder'>{{ their_key_unknown }}</div>
{{ /has_their_key }}
@ -332,6 +321,8 @@
{{ #chunks }} <span>{{ . }}</span> {{ /chunks }}
</div>
{{ /has_their_key }}
<p> {{> link_to_support }} </p>
</div>
</script>
<!-- index -->
<script type='text/x-tmpl-mustache' id='group_info_input'>
@ -400,24 +391,6 @@
{{ learnMore }}
</a>
</script>
<script type='text/x-tmpl-mustache' id='key-conflict-dialogue'>
<h3 class='header'>{{ newIdentity }}</h3>
<div class='content clearfix'>
<div class='clearfix'>
{{> avatar }}
<span class='name'>{{ name }}</span>
<button class='resolve'>{{ resolve }}</button>
<a href='#' class='hideKeys hide'> {{ hideKeys }} </a>
<a href='#' class='showKeys'> {{ showKeys }} </a>
</div>
<div class='keys hide'>
<p>
{{ message }}
{{> link_to_support }}
</p>
</div>
</div>
</script>
<script type='text/x-tmpl-mustache' id='debug-log'>
<div class='content'>
<div>
@ -656,7 +629,6 @@
<script type='text/javascript' src='js/views/contact_list_view.js'></script>
<script type='text/javascript' src='js/views/new_group_update_view.js'></script>
<script type='text/javascript' src='js/views/attachment_view.js'></script>
<script type='text/javascript' src='js/views/key_conflict_dialogue_view.js'></script>
<script type='text/javascript' src='js/views/error_view.js'></script>
<script type='text/javascript' src='js/views/timestamp_view.js'></script>
<script type='text/javascript' src='js/views/message_view.js'></script>

View file

@ -512,39 +512,6 @@
}.bind(this));
},
resolveConflicts: function(conflict) {
var number = conflict.number;
var identityKey = conflict.identityKey;
if (this.isPrivate()) {
number = this.id;
} else if (!_.include(this.get('members'), number)) {
throw 'Tried to resolve conflicts for a unknown group member';
}
if (!this.messageCollection.hasKeyConflicts()) {
throw 'No conflicts to resolve';
}
return textsecure.storage.protocol.saveIdentity(number, identityKey, true).then(function() {
var promise = Promise.resolve();
var conflicts = this.messageCollection.filter(function(message) {
return message.hasKeyConflict(number);
});
// group incoming & outgoing
conflicts = _.groupBy(conflicts, function(m) { return m.get('type'); });
// sort each group by date and concatenate outgoing after incoming
conflicts = _.flatten([
_.sortBy(conflicts.incoming, function(m) { return m.get('received_at'); }),
_.sortBy(conflicts.outgoing, function(m) { return m.get('received_at'); }),
]).forEach(function(message) {
var resolveConflict = function() {
return message.resolveConflict(number);
};
promise = promise.then(resolveConflict, resolveConflict);
});
return promise;
}.bind(this));
},
notify: function(message) {
if (!message.isIncoming()) {
return;

View file

@ -88,9 +88,6 @@
if (this.isEndSession()) {
return i18n('sessionEnded');
}
if (this.isIncoming() && this.hasKeyConflicts()) {
return i18n('incomingKeyConflict');
}
if (this.isIncoming() && this.hasErrors()) {
return i18n('incomingError');
}
@ -191,26 +188,6 @@
hasErrors: function() {
return _.size(this.get('errors')) > 0;
},
hasKeyConflicts: function() {
return _.any(this.get('errors'), function(e) {
return (e.name === 'IncomingIdentityKeyError' ||
e.name === 'OutgoingIdentityKeyError');
});
},
hasKeyConflict: function(number) {
return _.any(this.get('errors'), function(e) {
return (e.name === 'IncomingIdentityKeyError' ||
e.name === 'OutgoingIdentityKeyError') &&
e.number === number;
});
},
getKeyConflict: function(number) {
return _.find(this.get('errors'), function(e) {
return (e.name === 'IncomingIdentityKeyError' ||
e.name === 'OutgoingIdentityKeyError') &&
e.number === number;
});
},
send: function(promise) {
this.trigger('pending');
@ -281,15 +258,6 @@
return this.save({errors : errors});
},
removeConflictFor: function(number) {
var errors = _.reject(this.get('errors'), function(e) {
return e.number === number &&
(e.name === 'IncomingIdentityKeyError' ||
e.name === 'OutgoingIdentityKeyError');
});
this.set({errors: errors});
},
hasNetworkError: function(number) {
var error = _.find(this.get('errors'), function(e) {
return (e.name === 'MessageError' ||
@ -325,30 +293,6 @@
}
},
resolveConflict: function(number) {
var error = this.getKeyConflict(number);
if (error) {
this.removeConflictFor(number);
var promise = new textsecure.ReplayableError(error).replay();
if (this.isIncoming()) {
promise = promise.then(function(dataMessage) {
this.removeConflictFor(number);
this.handleDataMessage(dataMessage);
}.bind(this));
} else {
promise = this.send(promise).then(function() {
this.removeConflictFor(number);
this.save();
}.bind(this));
}
promise.catch(function(e) {
this.removeConflictFor(number);
this.saveErrors(e);
}.bind(this));
return promise;
}
},
handleDataMessage: function(dataMessage) {
// This function can be called from the background script on an
// incoming message or from the frontend after the user accepts an
@ -640,10 +584,6 @@
conditions: { expires_at: { $lte: Date.now() } },
addIndividually: true
});
},
hasKeyConflicts: function() {
return this.any(function(m) { return m.hasKeyConflicts(); });
}
});
})();

View file

@ -5,6 +5,7 @@
'use strict';
window.Whisper = window.Whisper || {};
// Contact list view is used in the list group members senario, as well as the NewGroupUpdate view
Whisper.ContactListView = Whisper.ListView.extend({
tagName: 'div',
itemView: Whisper.View.extend({

View file

@ -13,48 +13,4 @@
return this.model;
}
});
var KeyConflictView = ErrorView.extend({
className: 'key-conflict',
templateName: 'key-conflict',
initialize: function(options) {
this.message = options.message;
},
events: {
'click': 'select'
},
render_attributes: function() {
var errorMessage;
if (this.message.isIncoming()) {
errorMessage = 'incomingKeyConflict';
} else {
errorMessage = 'outgoingKeyConflict';
}
return { message: i18n(errorMessage) };
},
select: function() {
this.$el.trigger('select', {message: this.message});
},
});
Whisper.MessageErrorView = Backbone.View.extend({
className: 'error',
initialize: function(options) {
if (this.model.name === 'IncomingIdentityKeyError' ||
this.model.name === 'OutgoingIdentityKeyError') {
this.view = new KeyConflictView({
model : this.model,
message : options.message
});
} else {
this.view = new ErrorView({ model: this.model });
}
this.$el.append(this.view.el);
this.view.render();
},
render: function() {
this.view.render();
return this;
}
});
})();

View file

@ -5,6 +5,11 @@
'use strict';
window.Whisper = window.Whisper || {};
// This needs to make each member link to their verification view - except for yourself
// Do we update the display of each user to add Verified to their name if verified?
// What about the case where we're brought here because there are multiple users in the no-longer-verified state?
// We probably want to display some sort of helper text in that case at the least
// Or do we show only the problematic users in that case?
Whisper.GroupMemberList = Whisper.View.extend({
className: 'group-member-list panel',
templateName: 'group-member-list',

View file

@ -1,53 +0,0 @@
/*
* vim: ts=4:sw=4:expandtab
*/
(function () {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.KeyConflictDialogueView = Whisper.View.extend({
templateName: 'key-conflict-dialogue',
className: 'key-conflict-dialogue clearfix',
initialize: function(options) {
this.contact = options.contact;
this.conversation = options.conversation;
this.render();
var view = new Whisper.KeyVerificationView({
model : this.contact,
newKey : this.model.identityKey
});
view.$el.appendTo(this.$('.keys'));
},
events: {
'click .showKeys': 'showKeys',
'click .hideKeys': 'hideKeys',
'click .resolve' : 'resolve'
},
hideKeys: function() {
this.$('.keys, .hideKeys').hide();
this.$('.showKeys').show();
},
showKeys: function() {
this.$('.keys, .hideKeys').show();
this.$('.showKeys').hide();
},
resolve: function() {
this.remove();
this.conversation.resolveConflicts(this.model);
},
render_attributes: function() {
return {
name : this.contact.getTitle(),
avatar : this.contact.getAvatar(),
conflict : this.model,
newIdentity : i18n('newIdentity'),
message : i18n('identityChanged'),
resolve : i18n('acceptNewKey'),
showKeys : i18n('showMore'),
hideKeys : i18n('showLess'),
learnMore : i18n('learnMore')
};
}
});
})();

View file

@ -5,9 +5,11 @@
'use strict';
window.Whisper = window.Whisper || {};
// TODO; find all uses of that removed panel
// Add the Verify functionality to this view
Whisper.KeyVerificationView = Whisper.View.extend({
className: 'key-verification',
templateName: 'key_verification',
className: 'key-verification panel',
templateName: 'key-verification',
initialize: function(options) {
this.our_number = textsecure.storage.user.getNumber();
if (options.newKey) {
@ -21,6 +23,8 @@
//.then(this.makeQRCode.bind(this));
},
makeQRCode: function() {
// Per Lilia: We can't turn this on until it geneates a Latin1 string, as is
// required by the mobile clients.
new QRCode(this.$('.qr')[0]).makeCode(
dcodeIO.ByteBuffer.wrap(this.our_key).toString('base64')
);
@ -72,8 +76,4 @@
};
}
});
Whisper.KeyVerificationPanelView = Whisper.KeyVerificationView.extend({
className: 'key-verification panel',
templateName: 'key_verification_panel',
});
})();

View file

@ -9,7 +9,6 @@
className: 'contact-detail',
templateName: 'contact-detail',
initialize: function(options) {
this.conflict = options.conflict;
this.errors = _.reject(options.errors, function(e) {
return (e.name === 'IncomingIdentityKeyError' ||
e.name === 'OutgoingIdentityKeyError' ||
@ -51,19 +50,6 @@
errors: this.errors[contact.id]
}).render();
this.$('.contacts').append(view.el);
var conflict = this.model.getKeyConflict(contact.id);
if (conflict) {
this.renderConflict(contact, conflict);
}
},
renderConflict: function(contact, conflict) {
var view = new Whisper.KeyConflictDialogueView({
model: conflict,
contact: contact,
conversation: this.conversation
});
this.$('.conflicts').append(view.el);
},
render: function() {
this.errors = _.groupBy(this.model.get('errors'), 'number');
@ -81,8 +67,7 @@
title : i18n('messageDetail'),
sent : i18n('sent'),
received : i18n('received'),
errorLabel : i18n('error'),
hasConflict : this.model.hasKeyConflicts()
errorLabel : i18n('error')
}));
this.view.$el.prependTo(this.$('.message-container'));

View file

@ -5,6 +5,7 @@
'use strict';
window.Whisper = window.Whisper || {};
// When is this used?
Whisper.NewGroupUpdateView = Whisper.View.extend({
tagName: "div",
className: 'new-group-update',

View file

@ -106,40 +106,6 @@
.message-detail {
background-color: #eee;
.key-conflict-dialogue {
border-radius: $border-radius;
margin: 20px 0;
.header {
border-radius: $border-radius $border-radius 0 0;
background: #F3F3A7;
margin: 0;
padding: 10px 20px
}
.content {
padding: 20px;
border: 2px solid #F3F3A7;
}
button.resolve {
outline: none;
border: none;
border-radius: $border-radius;
color: white;
font-weight: bold;
line-height: 36px;
padding: 0 20px;
float: right;
background: $blue;
margin-left: 20px;
}
.hideKeys, .showKeys {
float: right;
line-height: 36px;
}
}
.message-container {
padding: 20px 0;
@ -192,10 +158,6 @@
padding: 5px;
}
button.conflict {
float: right;
background: #d00;
}
button.cancel {
float: right;
color: $grey_d;
@ -533,19 +495,10 @@ li.entry .error-icon-container {
cursor: pointer;
font-style: italic;
}
.key-conflict {
padding: 15px 10px;
button {
margin-top: 5px;
}
}
}
.message-list,
.message-container,
.key-conflict-dialogue {
.message-container {
.avatar {
height: 36px;
width: 36px;

View file

@ -1055,31 +1055,6 @@ input.search {
.message-detail {
background-color: #eee; }
.message-detail .key-conflict-dialogue {
border-radius: 5px;
margin: 20px 0; }
.message-detail .key-conflict-dialogue .header {
border-radius: 5px 5px 0 0;
background: #F3F3A7;
margin: 0;
padding: 10px 20px; }
.message-detail .key-conflict-dialogue .content {
padding: 20px;
border: 2px solid #F3F3A7; }
.message-detail .key-conflict-dialogue button.resolve {
outline: none;
border: none;
border-radius: 5px;
color: white;
font-weight: bold;
line-height: 36px;
padding: 0 20px;
float: right;
background: #2090ea;
margin-left: 20px; }
.message-detail .key-conflict-dialogue .hideKeys, .message-detail .key-conflict-dialogue .showKeys {
float: right;
line-height: 36px; }
.message-detail .message-container {
padding: 20px 0; }
.message-detail .message-container .sender {
@ -1111,9 +1086,6 @@ input.search {
.message-detail h3 {
font-size: 1em;
padding: 5px; }
.message-detail button.conflict {
float: right;
background: #d00; }
.message-detail button.cancel {
float: right;
color: #454545;
@ -1408,16 +1380,9 @@ li.entry .error-icon-container {
.message-list .bubble .content.error-message {
cursor: pointer;
font-style: italic; }
.message-container .key-conflict,
.message-list .key-conflict {
padding: 15px 10px; }
.message-container .key-conflict button,
.message-list .key-conflict button {
margin-top: 5px; }
.message-list .avatar,
.message-container .avatar,
.key-conflict-dialogue .avatar {
.message-container .avatar {
height: 36px;
width: 36px;
line-height: 36px; }

View file

@ -264,12 +264,6 @@
</script>
<script type='text/x-tmpl-mustache' id='message-detail'>
<div class='container'>
{{ #hasConflict }}
<div class='hasConflict clearfix'>
<div class='conflicts'>
</div>
</div>
{{ /hasConflict }}
<div class='message-container'></div>
<div class='info'>
<table>
@ -299,33 +293,20 @@
<script type='text/x-tmpl-mustache' id='group-member-list'>
<div class='container'></div>
</script>
<script type='text/x-tmpl-mustache' id='key_verification_panel'>
<script type='text/x-tmpl-mustache' id='key-verification'>
<div class='container'>
{{> key_verification }}
<p> {{> link_to_support }} </p>
</div>
</script>
<script type='text/x-tmpl-mustache' id='key_verification'>
<label> {{theirIdentity}} </label>
{{ ^their_key }}
{{ ^has_their_key }}
<div class='placeholder'>{{ their_key_unknown }}</div>
{{ /their_key }}
{{ /has_their_key }}
{{ #has_their_key }}
<label> {{ yourSafetyNumberWith }} </label>
<!--<div class='qr'></div>-->
<div class='key'>
{{ #their_key }} <span>{{ . }}</span> {{ /their_key }}
{{ #chunks }} <span>{{ . }}</span> {{ /chunks }}
</div>
{{ /has_their_key }}
<label> {{yourIdentity}} </label>
<div class='key'>
{{ #your_key }} <span>{{ . }}</span> {{ /your_key }}
</div>
<div class='securityNumber'></div>
</script>
<script type='text/x-tmpl-mustache' id='security_number'>
<label> Security number </label>
<div class='key'>
{{ #chunks }} <span>{{ . }}</span> {{ /chunks }}
</div>
<p> {{> link_to_support }} </p>
</div>
</script>
<!-- index -->
<script type='text/x-tmpl-mustache' id='group_info_input'>
@ -394,24 +375,6 @@
{{ learnMore }}
</a>
</script>
<script type='text/x-tmpl-mustache' id='key-conflict-dialogue'>
<h3 class='header'>{{ newIdentity }}</h3>
<div class='content clearfix'>
<div class='clearfix'>
{{> avatar }}
<span class='name'>{{ name }}</span>
<button class='resolve'>{{ resolve }}</button>
<a href='#' class='hideKeys hide'> {{ hideKeys }} </a>
<a href='#' class='showKeys'> {{ showKeys }} </a>
</div>
<div class='keys hide'>
<p>
{{ message }}
{{> link_to_support }}
</p>
</div>
</div>
</script>
<script type='text/x-tmpl-mustache' id='debug-log'>
<div class='content'>
<div>
@ -559,7 +522,6 @@
<script type='text/javascript' src='../js/views/new_group_update_view.js' data-cover></script>
<script type="text/javascript" src="../js/views/group_update_view.js"></script>
<script type='text/javascript' src='../js/views/attachment_view.js' data-cover></script>
<script type='text/javascript' src='../js/views/key_conflict_dialogue_view.js' data-cover></script>
<script type='text/javascript' src='../js/views/error_view.js' data-cover></script>
<script type='text/javascript' src='../js/views/timestamp_view.js' data-cover></script>
<script type='text/javascript' src='../js/views/message_view.js' data-cover></script>

View file

@ -136,27 +136,11 @@
assert.ok(message.isGroupUpdate());
});
it('checks if there are any key conflicts', function() {
var messages = new Whisper.MessageCollection();
var message = messages.add(attributes);
assert.notOk(message.hasKeyConflicts());
message = messages.add({errors: [{name: 'OutgoingIdentityKeyError'}]});
assert.ok(message.hasKeyConflicts());
});
it('returns a description of key conflicts', function() {
var messages = new Whisper.MessageCollection();
var message = messages.add({errors: [{name: 'IncomingIdentityKeyError'}]});
assert.deepEqual(message.getKeyConflict(), {name: 'IncomingIdentityKeyError'});
});
it('returns an accurate description', function() {
var messages = new Whisper.MessageCollection();
var message = messages.add(attributes);
assert.equal(message.getDescription(), 'hi', 'If no group updates, key conflicts, or end session flags, return message body.');
assert.equal(message.getDescription(), 'hi', 'If no group updates or end session flags, return message body.');
message = messages.add({group_update: {left: 'Alice'}});
assert.equal(message.getDescription(), 'Alice left the group.', 'Notes one person leaving the group.');
@ -175,10 +159,6 @@
message = messages.add({flags: true});
assert.equal(message.getDescription(), i18n('sessionEnded'));
message = messages.add({type: 'incoming', errors: [{name: 'OutgoingIdentityKeyError'}]});
assert.equal(message.getDescription(), i18n('incomingKeyConflict'));
});
it('checks if it is end of the session', function() {