From 91ee5f8a423c274156985042470ed255faa3a00d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 2 Dec 2015 14:59:37 +0000 Subject: [PATCH 01/28] Add extra arg isGuest to MatrixClientPeg functions. Add GuestAccess class GuestAccess serves to monitor which rooms are being tracked for guest purposes --- src/GuestAccess.js | 52 ++++++++++++++++++++++++++++++++++++++++++ src/MatrixClientPeg.js | 20 ++++++++++++---- 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/GuestAccess.js diff --git a/src/GuestAccess.js b/src/GuestAccess.js new file mode 100644 index 0000000000..a76c2f2169 --- /dev/null +++ b/src/GuestAccess.js @@ -0,0 +1,52 @@ +/* +Copyright 2015 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import {MatrixClientPeg} from "./MatrixClientPeg"; +const ROOM_ID_KEY = "matrix-guest-room-ids"; + +class GuestAccess { + + constructor(localStorage) { + var existingRoomIds; + try { + existingRoomIds = JSON.parse( + localStorage.getItem(ROOM_ID_KEY) // an array + ); + } + catch (e) {} // don't care + this.rooms = new Set(existingRoomIds); + this.localStorage = localStorage; + } + + addRoom(roomId) { + this.rooms.add(roomId); + } + + removeRoom(roomId) { + this.rooms.delete(roomId); + } + + getRooms() { + return this.rooms.entries(); + } + + register() { + // nuke the rooms being watched from previous guest accesses if any. + localStorage.setItem(ROOM_ID_KEY, "[]"); + return MatrixClientPeg.get().registerGuest(); + } +} + +module.exports = GuestAccess; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index d940b69f27..21fe934ca5 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -33,7 +33,7 @@ function deviceId() { return id; } -function createClient(hs_url, is_url, user_id, access_token) { +function createClient(hs_url, is_url, user_id, access_token, isGuest) { var opts = { baseUrl: hs_url, idBaseUrl: is_url, @@ -47,6 +47,9 @@ function createClient(hs_url, is_url, user_id, access_token) { } matrixClient = Matrix.createClient(opts); + if (isGuest) { + matrixClient.setGuest(true); + } } if (localStorage) { @@ -54,8 +57,16 @@ if (localStorage) { var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; var access_token = localStorage.getItem("mx_access_token"); var user_id = localStorage.getItem("mx_user_id"); + var isGuest = localStorage.getItem("mx_is_guest") || false; + if (isGuest) { + try { + isGuest = JSON.parse(isGuest); + } + catch (e) {} // ignore + isGuest = Boolean(isGuest); // in case of null, make it false. + } if (access_token && user_id && hs_url) { - createClient(hs_url, is_url, user_id, access_token); + createClient(hs_url, is_url, user_id, access_token, isGuest); } } @@ -97,7 +108,7 @@ class MatrixClient { } } - replaceUsingAccessToken(hs_url, is_url, user_id, access_token) { + replaceUsingAccessToken(hs_url, is_url, user_id, access_token, isGuest) { if (localStorage) { try { localStorage.clear(); @@ -105,13 +116,14 @@ class MatrixClient { console.warn("Error using local storage"); } } - createClient(hs_url, is_url, user_id, access_token); + createClient(hs_url, is_url, user_id, access_token, isGuest); if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); localStorage.setItem("mx_is_url", is_url); localStorage.setItem("mx_user_id", user_id); localStorage.setItem("mx_access_token", access_token); + localStorage.setItem("mx_is_guest", isGuest); } catch (e) { console.warn("Error using local storage: can't persist session!"); } From d52b3149d4f074d9e66fd0f1b6268410bcc67250 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 10:37:53 +0000 Subject: [PATCH 02/28] Wire GuestAccess to MatrixClientPeg --- src/GuestAccess.js | 28 ++++++++++++++++++++++++---- src/MatrixClientPeg.js | 31 ++++++++++++++++--------------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/GuestAccess.js b/src/GuestAccess.js index a76c2f2169..b1a059425d 100644 --- a/src/GuestAccess.js +++ b/src/GuestAccess.js @@ -15,12 +15,14 @@ limitations under the License. */ import {MatrixClientPeg} from "./MatrixClientPeg"; const ROOM_ID_KEY = "matrix-guest-room-ids"; +const IS_GUEST_KEY = "matrix-is-guest"; class GuestAccess { constructor(localStorage) { var existingRoomIds; try { + this._isGuest = localStorage.getItem(IS_GUEST_KEY) === "true"; existingRoomIds = JSON.parse( localStorage.getItem(ROOM_ID_KEY) // an array ); @@ -32,20 +34,38 @@ class GuestAccess { addRoom(roomId) { this.rooms.add(roomId); + this._saveAndSetRooms(); } removeRoom(roomId) { this.rooms.delete(roomId); + this._saveAndSetRooms(); } getRooms() { return this.rooms.entries(); } - register() { - // nuke the rooms being watched from previous guest accesses if any. - localStorage.setItem(ROOM_ID_KEY, "[]"); - return MatrixClientPeg.get().registerGuest(); + isGuest() { + return this._isGuest; + } + + markAsGuest(isGuest) { + try { + this.localStorage.setItem(IS_GUEST_KEY, JSON.stringify(isGuest)); + // nuke the rooms being watched from previous guest accesses if any. + this.localStorage.setItem(ROOM_ID_KEY, "[]"); + } catch (e) {} // ignore. If they don't do LS, they'll just get a new account. + this._isGuest = isGuest; + this.rooms = new Set(); + } + + _saveAndSetRooms() { + let rooms = this.getRooms(); + MatrixClientPeg.get().setGuestRooms(rooms); + try { + this.localStorage.setItem(ROOM_ID_KEY, rooms); + } catch (e) {} } } diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 21fe934ca5..7a1ee781ab 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -18,6 +18,7 @@ limitations under the License. // A thing that holds your Matrix Client var Matrix = require("matrix-js-sdk"); +var GuestAccess = require("./GuestAccess"); var matrixClient = null; @@ -33,7 +34,7 @@ function deviceId() { return id; } -function createClient(hs_url, is_url, user_id, access_token, isGuest) { +function createClient(hs_url, is_url, user_id, access_token, guestAccess) { var opts = { baseUrl: hs_url, idBaseUrl: is_url, @@ -47,8 +48,10 @@ function createClient(hs_url, is_url, user_id, access_token, isGuest) { } matrixClient = Matrix.createClient(opts); - if (isGuest) { - matrixClient.setGuest(true); + if (guestAccess) { + console.log("Guest: %s", guestAccess.isGuest()); + matrixClient.setGuest(guestAccess.isGuest()); + matrixClient.setGuestRooms(guestAccess.getRooms()); } } @@ -57,20 +60,18 @@ if (localStorage) { var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; var access_token = localStorage.getItem("mx_access_token"); var user_id = localStorage.getItem("mx_user_id"); - var isGuest = localStorage.getItem("mx_is_guest") || false; - if (isGuest) { - try { - isGuest = JSON.parse(isGuest); - } - catch (e) {} // ignore - isGuest = Boolean(isGuest); // in case of null, make it false. - } + var guestAccess = new GuestAccess(localStorage); if (access_token && user_id && hs_url) { - createClient(hs_url, is_url, user_id, access_token, isGuest); + createClient(hs_url, is_url, user_id, access_token, guestAccess); } } class MatrixClient { + + constructor(guestAccess) { + this.guestAccess = guestAccess; + } + get() { return matrixClient; } @@ -116,14 +117,14 @@ class MatrixClient { console.warn("Error using local storage"); } } - createClient(hs_url, is_url, user_id, access_token, isGuest); + this.guestAccess.markAsGuest(isGuest); + createClient(hs_url, is_url, user_id, access_token, this.guestAccess); if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); localStorage.setItem("mx_is_url", is_url); localStorage.setItem("mx_user_id", user_id); localStorage.setItem("mx_access_token", access_token); - localStorage.setItem("mx_is_guest", isGuest); } catch (e) { console.warn("Error using local storage: can't persist session!"); } @@ -134,6 +135,6 @@ class MatrixClient { } if (!global.mxMatrixClient) { - global.mxMatrixClient = new MatrixClient(); + global.mxMatrixClient = new MatrixClient(new GuestAccess(localStorage)); } module.exports = global.mxMatrixClient; From d0ec84fe591734645a27bf5d0c687271a58a796b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 11:34:50 +0000 Subject: [PATCH 03/28] Hook up auto-registration as a guest to MatrixChat. --- src/MatrixClientPeg.js | 2 +- src/components/structures/MatrixChat.js | 52 +++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 7a1ee781ab..76fa102562 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -117,7 +117,7 @@ class MatrixClient { console.warn("Error using local storage"); } } - this.guestAccess.markAsGuest(isGuest); + this.guestAccess.markAsGuest(Boolean(isGuest)); createClient(hs_url, is_url, user_id, access_token, this.guestAccess); if (localStorage) { try { diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index a6d6716222..6bb16dafd2 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -40,7 +40,8 @@ module.exports = React.createClass({ config: React.PropTypes.object.isRequired, ConferenceHandler: React.PropTypes.any, onNewScreen: React.PropTypes.func, - registrationUrl: React.PropTypes.string + registrationUrl: React.PropTypes.string, + enableGuest: React.PropTypes.bool }, PageTypes: { @@ -75,15 +76,31 @@ module.exports = React.createClass({ }, componentDidMount: function() { + this._autoRegisterAsGuest = false; + if (this.props.enableGuest) { + if (!this.props.config || !this.props.config.default_hs_url) { + console.error("Cannot enable guest access: No supplied config prop for HS/IS URLs"); + } + else { + this._autoRegisterAsGuest = true; + } + } + this.dispatcherRef = dis.register(this.onAction); if (this.state.logged_in) { + // Don't auto-register as a guest. This applies if you refresh the page on a + // logged in client THEN hit the Sign Out button. + this._autoRegisterAsGuest = false; this.startMatrixClient(); } this.focusComposer = false; document.addEventListener("keydown", this.onKeyDown); window.addEventListener("focus", this.onFocus); + if (this.state.logged_in) { this.notifyNewScreen(''); + } else if (this._autoRegisterAsGuest) { + this._registerAsGuest(); } else { this.notifyNewScreen('login'); } @@ -115,6 +132,26 @@ module.exports = React.createClass({ } }, + _registerAsGuest: function() { + var self = this; + var config = this.props.config; + console.log("Doing guest login on %s", config.default_hs_url); + MatrixClientPeg.replaceUsingUrls( + config.default_hs_url, config.default_is_url + ); + MatrixClientPeg.get().registerGuest().done(function(creds) { + console.log("Registered as guest: %s", JSON.stringify(creds)); + }, function(err) { + console.error(err.data); + self._setAutoRegisterAsGuest(false); + }); + }, + + _setAutoRegisterAsGuest: function(shouldAutoRegister) { + this._autoRegisterAsGuest = shouldAutoRegister; + this.forceUpdate(); + }, + onAction: function(payload) { var roomIndexDelta = 1; @@ -130,6 +167,7 @@ module.exports = React.createClass({ MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.unset(); + this.notifyNewScreen('login'); this.replaceState({ logged_in: false, @@ -634,12 +672,20 @@ module.exports = React.createClass({ ); } - } else if (this.state.logged_in) { + } else if (this.state.logged_in || (!this.state.logged_in && this._autoRegisterAsGuest)) { var Spinner = sdk.getComponent('elements.Spinner'); + var logoutLink; + if (this.state.logged_in) { + logoutLink = ( + + Logout + + ); + } return (
- Logout + {logoutLink}
); } else if (this.state.screen == 'register') { From 92cf14d5e9dbf864b2619225de1140c54ee46b8e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 15:28:35 +0000 Subject: [PATCH 04/28] Support registration as a guest --- src/GuestAccess.js | 2 +- src/components/structures/MatrixChat.js | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/GuestAccess.js b/src/GuestAccess.js index b1a059425d..a76e198b4e 100644 --- a/src/GuestAccess.js +++ b/src/GuestAccess.js @@ -43,7 +43,7 @@ class GuestAccess { } getRooms() { - return this.rooms.entries(); + return Array.from(this.rooms.entries()); } isGuest() { diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 6bb16dafd2..4af6e96c96 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -140,7 +140,14 @@ module.exports = React.createClass({ config.default_hs_url, config.default_is_url ); MatrixClientPeg.get().registerGuest().done(function(creds) { - console.log("Registered as guest: %s", JSON.stringify(creds)); + console.log("Registered as guest: %s", creds.user_id); + self.onLoggedIn({ + userId: creds.user_id, + accessToken: creds.access_token, + homeserverUrl: config.default_hs_url, + identityServerUrl: config.default_is_url, + guest: true + }); }, function(err) { console.error(err.data); self._setAutoRegisterAsGuest(false); @@ -361,10 +368,11 @@ module.exports = React.createClass({ }, onLoggedIn: function(credentials) { - console.log("onLoggedIn => %s", credentials.userId); + credentials.guest = Boolean(credentials.guest); + console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest); MatrixClientPeg.replaceUsingAccessToken( credentials.homeserverUrl, credentials.identityServerUrl, - credentials.userId, credentials.accessToken + credentials.userId, credentials.accessToken, credentials.guest ); this.setState({ screen: undefined, From 8bd4fdbd5a98d9a2aa812e6c6e8f949b2736a073 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 5 Jan 2016 13:24:05 +0000 Subject: [PATCH 05/28] Don't set presence --- src/Presence.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Presence.js b/src/Presence.js index e776cca078..c3d6857e95 100644 --- a/src/Presence.js +++ b/src/Presence.js @@ -73,6 +73,11 @@ class Presence { } var old_state = this.state; this.state = newState; + + if (MatrixClientPeg.get().isGuest()) { + return; // don't try to set presence when a guest; it won't work. + } + var self = this; MatrixClientPeg.get().setPresence(this.state).done(function() { console.log("Presence: %s", newState); From d729dee31db5ce504133dc2c900f39473f9fc34a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 5 Jan 2016 17:34:25 +0000 Subject: [PATCH 06/28] Make the settings page load for guests. Add checkboxes for guest r/w --- src/UserSettingsStore.js | 7 ++- src/components/structures/RoomView.js | 52 ++++++++++++---------- src/components/views/rooms/RoomSettings.js | 20 +++++++++ 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index f2ada35b24..55a0518d85 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -15,7 +15,7 @@ limitations under the License. */ 'use strict'; - +var q = require("q"); var MatrixClientPeg = require("./MatrixClientPeg"); var Notifier = require("./Notifier"); @@ -35,6 +35,11 @@ module.exports = { }, loadThreePids: function() { + if (MatrixClientPeg.get().isGuest()) { + return q({ + threepids: [] + }); // guests can't poke 3pid endpoint + } return MatrixClientPeg.get().getThreePids(); }, diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 56dd41d929..318489d9fd 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -733,7 +733,7 @@ module.exports = React.createClass({ return ret; }, - uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { + uploadNewState: function(newVals) { var old_name = this.state.room.name; var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); @@ -759,46 +759,54 @@ module.exports = React.createClass({ var deferreds = []; - if (old_name != new_name && new_name != undefined && new_name) { + if (old_name != newVals.name && newVals.name != undefined && newVals.name) { deferreds.push( - MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) + MatrixClientPeg.get().setRoomName(this.state.room.roomId, newVals.name) ); } - if (old_topic != new_topic && new_topic != undefined) { + if (old_topic != newVals.topic && newVals.topic != undefined) { deferreds.push( - MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) + MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, newVals.topic) ); } - if (old_join_rule != new_join_rule && new_join_rule != undefined) { + if (old_join_rule != newVals.join_rule && newVals.join_rule != undefined) { deferreds.push( MatrixClientPeg.get().sendStateEvent( this.state.room.roomId, "m.room.join_rules", { - join_rule: new_join_rule, + join_rule: newVals.join_rule, }, "" ) ); } - if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { + if (old_history_visibility != newVals.history_visibility && + newVals.history_visibility != undefined) { deferreds.push( MatrixClientPeg.get().sendStateEvent( this.state.room.roomId, "m.room.history_visibility", { - history_visibility: new_history_visibility, + history_visibility: newVals.history_visibility, }, "" ) ); } - if (new_power_levels) { + if (newVals.power_levels) { deferreds.push( MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + this.state.room.roomId, "m.room.power_levels", newVals.power_levels, "" ) ); } + deferreds.push( + MatrixClientPeg.get().setGuestAccess(this.state.room.roomId, { + allowRead: newVals.guest_read, + allowJoin: newVals.guest_join + }) + ); + if (deferreds.length) { var self = this; q.all(deferreds).fail(function(err) { @@ -883,19 +891,15 @@ module.exports = React.createClass({ uploadingRoomSettings: true, }); - var new_name = this.refs.header.getRoomName(); - var new_topic = this.refs.room_settings.getTopic(); - var new_join_rule = this.refs.room_settings.getJoinRules(); - var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); - var new_power_levels = this.refs.room_settings.getPowerLevels(); - - this.uploadNewState( - new_name, - new_topic, - new_join_rule, - new_history_visibility, - new_power_levels - ); + this.uploadNewState({ + name: this.refs.header.getRoomName(), + topic: this.refs.room_settings.getTopic(), + join_rule: this.refs.room_settings.getJoinRules(), + history_visibility: this.refs.room_settings.getHistoryVisibility(), + power_levels: this.refs.room_settings.getPowerLevels(), + guest_join: this.refs.room_settings.canGuestsJoin(), + guest_read: this.refs.room_settings.canGuestsRead() + }); }, onCancelClick: function() { diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index eb9bfd90c8..a242c47e4a 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -31,6 +31,14 @@ module.exports = React.createClass({ }; }, + canGuestsJoin: function() { + return this.refs.guests_join.checked; + }, + + canGuestsRead: function() { + return this.refs.guests_read.checked; + }, + getTopic: function() { return this.refs.topic.value; }, @@ -83,6 +91,10 @@ module.exports = React.createClass({ if (history_visibility) history_visibility = history_visibility.getContent().history_visibility; var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); + var guest_access = this.props.room.currentState.getStateEvents('m.room.guest_access', ''); + if (guest_access) { + guest_access = guest_access.getContent().guest_access; + } var events_levels = power_levels.events || {}; @@ -154,6 +166,14 @@ module.exports = React.createClass({