Merge pull request #3875 from matrix-org/zip/11880-message-shields
Verify individual messages via cross-signing
This commit is contained in:
commit
0809d7dcba
4 changed files with 66 additions and 12 deletions
|
@ -367,6 +367,11 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_e2eIcon_unknown {
|
||||||
|
background-image: url('$(res)/img/e2e/warning.svg');
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_e2eIcon_unencrypted {
|
.mx_EventTile_e2eIcon_unencrypted {
|
||||||
background-image: url('$(res)/img/e2e/warning.svg');
|
background-image: url('$(res)/img/e2e/warning.svg');
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -415,7 +420,8 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line {
|
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
|
||||||
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
|
||||||
padding-left: 60px;
|
padding-left: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,8 +433,13 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
|
||||||
border-left: $e2e-unverified-color 5px solid;
|
border-left: $e2e-unverified-color 5px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
|
||||||
|
border-left: $e2e-unknown-color 5px solid;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
|
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line {
|
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
|
||||||
|
.mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line {
|
||||||
padding-left: 78px;
|
padding-left: 78px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,14 +450,16 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
|
||||||
|
|
||||||
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp {
|
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
|
||||||
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
|
||||||
left: 3px;
|
left: 3px;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
|
||||||
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > .mx_EventTile_e2eIcon,
|
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > .mx_EventTile_e2eIcon,
|
||||||
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon {
|
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon,
|
||||||
|
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > .mx_EventTile_e2eIcon {
|
||||||
display: block;
|
display: block;
|
||||||
left: 41px;
|
left: 41px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,7 @@ $copy-button-url: "$(res)/img/icon_copy_message.svg";
|
||||||
|
|
||||||
// e2e
|
// e2e
|
||||||
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
|
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
|
||||||
|
$e2e-unknown-color: #e8bf37;
|
||||||
$e2e-unverified-color: #e8bf37;
|
$e2e-unverified-color: #e8bf37;
|
||||||
$e2e-warning-color: #ba6363;
|
$e2e-warning-color: #ba6363;
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,12 @@ const stateEventTileTypes = {
|
||||||
'm.room.related_groups': 'messages.TextualEvent',
|
'm.room.related_groups': 'messages.TextualEvent',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const E2E_STATE = {
|
||||||
|
VERIFIED: "verified",
|
||||||
|
WARNING: "warning",
|
||||||
|
UNKNOWN: "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
// Add all the Mjolnir stuff to the renderer
|
// Add all the Mjolnir stuff to the renderer
|
||||||
for (const evType of ALL_RULE_TYPES) {
|
for (const evType of ALL_RULE_TYPES) {
|
||||||
stateEventTileTypes[evType] = 'messages.TextualEvent';
|
stateEventTileTypes[evType] = 'messages.TextualEvent';
|
||||||
|
@ -235,6 +241,7 @@ export default createReactClass({
|
||||||
this._suppressReadReceiptAnimation = false;
|
this._suppressReadReceiptAnimation = false;
|
||||||
const client = this.context;
|
const client = this.context;
|
||||||
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
|
client.on("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||||
this.props.mxEvent.on("Event.decrypted", this._onDecrypted);
|
this.props.mxEvent.on("Event.decrypted", this._onDecrypted);
|
||||||
if (this.props.showReactions) {
|
if (this.props.showReactions) {
|
||||||
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
|
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
|
||||||
|
@ -260,6 +267,7 @@ export default createReactClass({
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
const client = this.context;
|
const client = this.context;
|
||||||
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
|
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||||
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
|
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
|
||||||
if (this.props.showReactions) {
|
if (this.props.showReactions) {
|
||||||
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
|
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
|
||||||
|
@ -282,18 +290,40 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onUserVerificationChanged: function(userId, _trustStatus) {
|
||||||
|
if (userId === this.props.mxEvent.getSender()) {
|
||||||
|
this._verifyEvent(this.props.mxEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_verifyEvent: async function(mxEvent) {
|
_verifyEvent: async function(mxEvent) {
|
||||||
if (!mxEvent.isEncrypted()) {
|
if (!mxEvent.isEncrypted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we directly trust the device, short-circuit here
|
||||||
const verified = await this.context.isEventSenderVerified(mxEvent);
|
const verified = await this.context.isEventSenderVerified(mxEvent);
|
||||||
|
if (verified) {
|
||||||
|
this.setState({
|
||||||
|
verified: E2E_STATE.VERIFIED,
|
||||||
|
}, () => {
|
||||||
|
// Decryption may have caused a change in size
|
||||||
|
this.props.onHeightChanged();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventSenderTrust = await this.context.checkEventSenderTrust(mxEvent);
|
||||||
|
if (!eventSenderTrust) {
|
||||||
|
this.setState({
|
||||||
|
verified: E2E_STATE.UNKNOWN,
|
||||||
|
}, this.props.onHeightChanged); // Decryption may have cause a change in size
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
verified: verified,
|
verified: eventSenderTrust.isVerified() ? E2E_STATE.VERIFIED : E2E_STATE.WARNING,
|
||||||
}, () => {
|
}, this.props.onHeightChanged); // Decryption may have caused a change in size
|
||||||
// Decryption may have caused a change in size
|
|
||||||
this.props.onHeightChanged();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_propsEqual: function(objA, objB) {
|
_propsEqual: function(objA, objB) {
|
||||||
|
@ -473,8 +503,10 @@ export default createReactClass({
|
||||||
|
|
||||||
// event is encrypted, display padlock corresponding to whether or not it is verified
|
// event is encrypted, display padlock corresponding to whether or not it is verified
|
||||||
if (ev.isEncrypted()) {
|
if (ev.isEncrypted()) {
|
||||||
if (this.state.verified) {
|
if (this.state.verified === E2E_STATE.VERIFIED) {
|
||||||
return; // no icon for verified
|
return; // no icon for verified
|
||||||
|
} else if (this.state.verified === E2E_STATE.UNKNOWN) {
|
||||||
|
return (<E2ePadlockUnknown />);
|
||||||
} else {
|
} else {
|
||||||
return (<E2ePadlockUnverified />);
|
return (<E2ePadlockUnverified />);
|
||||||
}
|
}
|
||||||
|
@ -604,8 +636,9 @@ export default createReactClass({
|
||||||
mx_EventTile_last: this.props.last,
|
mx_EventTile_last: this.props.last,
|
||||||
mx_EventTile_contextual: this.props.contextual,
|
mx_EventTile_contextual: this.props.contextual,
|
||||||
mx_EventTile_actionBarFocused: this.state.actionBarFocused,
|
mx_EventTile_actionBarFocused: this.state.actionBarFocused,
|
||||||
mx_EventTile_verified: !isBubbleMessage && this.state.verified === true,
|
mx_EventTile_verified: !isBubbleMessage && this.state.verified === E2E_STATE.VERIFIED,
|
||||||
mx_EventTile_unverified: !isBubbleMessage && this.state.verified === false,
|
mx_EventTile_unverified: !isBubbleMessage && this.state.verified === E2E_STATE.WARNING,
|
||||||
|
mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2E_STATE.UNKNOWN,
|
||||||
mx_EventTile_bad: isEncryptionFailure,
|
mx_EventTile_bad: isEncryptionFailure,
|
||||||
mx_EventTile_emote: msgtype === 'm.emote',
|
mx_EventTile_emote: msgtype === 'm.emote',
|
||||||
mx_EventTile_redacted: isRedacted,
|
mx_EventTile_redacted: isRedacted,
|
||||||
|
@ -901,6 +934,12 @@ function E2ePadlockUnencrypted(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function E2ePadlockUnknown(props) {
|
||||||
|
return (
|
||||||
|
<E2ePadlock title={_t("Encrypted by a deleted device")} icon="unknown" {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class E2ePadlock extends React.Component {
|
class E2ePadlock extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
icon: PropTypes.string.isRequired,
|
icon: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -905,6 +905,7 @@
|
||||||
"This message cannot be decrypted": "This message cannot be decrypted",
|
"This message cannot be decrypted": "This message cannot be decrypted",
|
||||||
"Encrypted by an unverified device": "Encrypted by an unverified device",
|
"Encrypted by an unverified device": "Encrypted by an unverified device",
|
||||||
"Unencrypted": "Unencrypted",
|
"Unencrypted": "Unencrypted",
|
||||||
|
"Encrypted by a deleted device": "Encrypted by a deleted device",
|
||||||
"Please select the destination room for this message": "Please select the destination room for this message",
|
"Please select the destination room for this message": "Please select the destination room for this message",
|
||||||
"Scroll to bottom of page": "Scroll to bottom of page",
|
"Scroll to bottom of page": "Scroll to bottom of page",
|
||||||
"Close preview": "Close preview",
|
"Close preview": "Close preview",
|
||||||
|
|
Loading…
Reference in a new issue