fallback to unencryted chat if initCrypto fails or on decryption error

This commit is contained in:
Sharon Kennedy 2020-03-04 22:05:09 -05:00
parent b934bb3a5f
commit 5569c4af63

View File

@ -24,12 +24,16 @@ const DEFAULT_ROOM_NAME = "Support Chat"
const BOT_USERNAME = "@help-bot:rhok.space"
const ENCRYPTION_CONFIG = { "algorithm": "m.megolm.v1.aes-sha2" };
const ENCRYPTION_NOTICE = "Messages in this chat are secured with end-to-end encryption."
const UNENCRYPTION_NOTICE = "End-to-end message encryption is not available on this browser."
const INTRO_MESSAGE = "This chat application does not collect any of your personal data or any data from your use of this service."
const AGREEMENT_MESSAGE = "👉 Do you want to continue? Type yes or no."
const CONFIRMATION_MESSAGE = "Waiting for a facilitator to join the chat..."
const RESTARTING_UNENCRYPTED_CHAT_MESSAGE = "Restarting chat without encryption."
const EXIT_MESSAGE = "The chat was not started."
const FACILITATOR_ROOM_ID = '!pYVVPyFKacZeKZbWyz:rhok.space'
const TERMS_URL="https://tosdr.org/"
const SUPPORT_SEEKER_DISPLAY_NAME="Anonymous"
const MATRIX_ERROR_MESSAGE = "There was an error in the messaging service. Please try again later."
const initialState = {
@ -172,6 +176,8 @@ class ChatBox extends React.Component {
username: username,
password: password,
localStorage: localStorage,
sessionId: sessionId,
deviceId: data.device_id,
})
// create new client with full options
@ -184,7 +190,7 @@ class ChatBox extends React.Component {
}
client = matrix.createClient(opts)
client.setDisplayName("Anonymous")
client.setDisplayName(SUPPORT_SEEKER_DISPLAY_NAME)
})
.catch(err => {
console.log("Registration error", err)
@ -196,9 +202,40 @@ class ChatBox extends React.Component {
client: client
})
})
.catch(err => this.initializeUnencryptedChat())
})
}
initializeUnencryptedChat = () => {
this.setState({ ready: false })
let opts = {
baseUrl: this.props.matrixServerUrl,
accessToken: this.state.accessToken,
userId: this.state.userId,
deviceId: this.state.deviceId
}
let client = matrix.createClient(opts)
client.setDisplayName(SUPPORT_SEEKER_DISPLAY_NAME)
return client.startClient()
.then(() => {
this.setState({
client: client,
isCryptoEnabled: false,
})
})
}
handleDecryptionError = () => {
this.displayBotMessage({ body: RESTARTING_UNENCRYPTED_CHAT_MESSAGE })
this.state.client.leave(this.state.roomId)
.then(() => this.state.client.stopClient())
.then(() => this.state.client.clearStores())
.then(() => this.initializeUnencryptedChat())
}
verifyAllRoomDevices = async function(roomId) {
let room = this.state.client.getRoom(roomId);
let members = (await room.getEncryptionTargetMembers()).map(x => x["userId"])
@ -211,44 +248,44 @@ class ChatBox extends React.Component {
}
}
createRoom = () => {
createRoom = async function() {
const currentDate = new Date()
const chatDate = currentDate.toLocaleDateString()
const chatTime = currentDate.toLocaleTimeString()
return this.state.client.createRoom({
let roomConfig = {
room_alias_name: `private-support-chat-${uuid()}`,
invite: [BOT_USERNAME],
visibility: 'private',
name: `${chatDate} - ${this.props.roomName} - started at ${chatTime}`,
initial_state: [
}
const isCryptoEnabled = await this.state.client.isCryptoEnabled()
if (isCryptoEnabled) {
roomConfig.initial_state = [
{
type: 'm.room.encryption',
state_key: '',
content: ENCRYPTION_CONFIG,
},
]
})
.then(data => {
this.verifyAllRoomDevices(data.room_id)
this.state.client.setPowerLevel(data.room_id, BOT_USERNAME, 100)
.then(() => console.log("Set bot power level to 100"))
.catch(err => console.log("Error setting bot power level", err))
const confirmationMsg = {
id: 'confirmation-msg-id',
type: 'm.room.message',
sender: BOT_USERNAME,
content: { body: CONFIRMATION_MESSAGE },
}
const messages = [...this.state.messages]
messages.push(confirmationMsg)
const { room_id } = await this.state.client.createRoom(roomConfig)
this.state.client.setPowerLevel(room_id, BOT_USERNAME, 100)
if (isCryptoEnabled) {
this.verifyAllRoomDevices(room_id)
} else {
this.displayBotMessage({ body: UNENCRYPTION_NOTICE })
}
this.displayBotMessage({ body: CONFIRMATION_MESSAGE })
this.setState({
roomId: data.room_id,
messages
})
})
.catch(err => {
console.log("Unable to create room", err)
roomId: room_id,
isCryptoEnabled
})
}
@ -274,6 +311,34 @@ class ChatBox extends React.Component {
})
}
displayFakeMessage = (content, sender) => {
const msgList = [...this.state.messages]
const msg = {
id: uuid(),
type: 'm.room.message',
sender: sender,
roomId: this.state.roomId,
content: content,
}
msgList.push(msg)
this.setState({ messages: msgList })
}
displayBotMessage = (content, roomId) => {
const msgList = [...this.state.messages]
const msg = {
id: uuid(),
type: 'm.room.message',
sender: BOT_USERNAME,
roomId: roomId || this.state.roomId,
content: content,
}
msgList.push(msg)
this.setState({ messages: msgList })
}
handleMessageEvent = event => {
const message = {
id: event.getId(),
@ -283,8 +348,6 @@ class ChatBox extends React.Component {
content: event.getContent(),
}
console.log("INCOMING MESSAGE", message)
const messages = [...this.state.messages]
messages.push(message)
this.setState({ messages })
@ -309,35 +372,34 @@ class ChatBox extends React.Component {
this.state.client.on("Room.timeline", (event, room, toStartOfTimeline) => {
if (event.getType() === "m.room.encryption") {
const msgList = [...this.state.messages]
const encryptionMsg = {
id: 'encryption-msg-id',
type: 'm.room.message',
sender: BOT_USERNAME,
roomId: room.room_id,
content: { body: ENCRYPTION_NOTICE },
this.displayBotMessage({ body: ENCRYPTION_NOTICE }, room.room_id)
}
msgList.push(encryptionMsg)
this.setState({ messages: msgList })
if (event.getType() === "m.room.message" && !this.state.isCryptoEnabled) {
if (event.isEncrypted()) {
return;
}
this.handleMessageEvent(event)
}
});
this.state.client.on("Event.decrypted", (event) => {
this.state.client.on("Event.decrypted", (event, err) => {
if (err) {
return this.handleDecryptionError()
}
if (event.getType() === "m.room.message") {
this.handleMessageEvent(event)
}
});
this.state.client.on("RoomMember.typing", (event, member) => {
if (member.typing) {
if (member.typing && event.getRoomId() === this.state.roomId) {
this.setState({ typingStatus: `${member.name} is typing...`})
}
else {
this.setState({ typingStatus: null })
}
});
}
if (!prevState.ready && this.state.ready) {
@ -374,45 +436,21 @@ class ChatBox extends React.Component {
if (this.state.awaitingAgreement && !this.state.client) {
if (this.state.inputValue.toLowerCase() === 'yes') {
const fakeUserMsg = {
id: 'fake-msg-id',
type: 'm.room.message',
sender: 'from-me',
content: { body: this.state.inputValue },
}
const messages = [...this.state.messages]
messages.push(fakeUserMsg)
this.setState({ inputValue: "", messages })
this.displayFakeMessage({ body: this.state.inputValue }, 'from-me')
this.setState({ inputValue: "" })
return this.initializeChat()
} else {
const fakeUserMsg = {
id: 'fake-msg-id',
type: 'm.room.message',
sender: 'from-me',
content: { body: this.state.inputValue },
}
const exitMsg = {
id: 'exit-msg-id',
type: 'm.room.message',
sender: BOT_USERNAME,
content: { body: EXIT_MESSAGE },
}
const messages = [...this.state.messages]
messages.push(fakeUserMsg)
messages.push(exitMsg)
this.setState({ inputValue: "", messages })
this.displayFakeMessage({ body: this.state.inputValue }, 'from-me')
this.displayBotMessage({ body: EXIT_MESSAGE })
return this.setState({ inputValue: "" })
}
}
if (this.state.client && this.state.roomId) {
return this.sendMessage()
}
}
render() {