浏览代码

Add jwt module to react

master
Ilya Daynatovich 8 年前
父节点
当前提交
ab5c2e9ded

+ 3
- 1
conference.js 查看文件

273
 function maybeRedirectToWelcomePage(options) {
273
 function maybeRedirectToWelcomePage(options) {
274
     // if close page is enabled redirect to it, without further action
274
     // if close page is enabled redirect to it, without further action
275
     if (config.enableClosePage) {
275
     if (config.enableClosePage) {
276
+        const { isGuest } = APP.store.getState()['features/jwt'];
277
+
276
         // save whether current user is guest or not, before navigating
278
         // save whether current user is guest or not, before navigating
277
         // to close page
279
         // to close page
278
-        window.sessionStorage.setItem('guest', APP.tokenData.isGuest);
280
+        window.sessionStorage.setItem('guest', isGuest);
279
         assignWindowLocationPathname('static/'
281
         assignWindowLocationPathname('static/'
280
                 + (options.feedbackSubmitted ? "close.html" : "close2.html"));
282
                 + (options.feedbackSubmitted ? "close.html" : "close2.html"));
281
         return;
283
         return;

+ 23
- 26
connection.js 查看文件

1
 /* global APP, JitsiMeetJS, config */
1
 /* global APP, JitsiMeetJS, config */
2
-const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
2
 
4
 import AuthHandler from './modules/UI/authentication/AuthHandler';
3
 import AuthHandler from './modules/UI/authentication/AuthHandler';
5
 import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
4
 import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
14
 
13
 
15
 const ConnectionEvents = JitsiMeetJS.events.connection;
14
 const ConnectionEvents = JitsiMeetJS.events.connection;
16
 const ConnectionErrors = JitsiMeetJS.errors.connection;
15
 const ConnectionErrors = JitsiMeetJS.errors.connection;
16
+const logger = require("jitsi-meet-logger").getLogger(__filename);
17
 
17
 
18
 /**
18
 /**
19
  * Checks if we have data to use attach instead of connect. If we have the data
19
  * Checks if we have data to use attach instead of connect. If we have the data
61
  * everything is ok, else error.
61
  * everything is ok, else error.
62
  */
62
  */
63
 function connect(id, password, roomName) {
63
 function connect(id, password, roomName) {
64
-
65
-    let connectionConfig = Object.assign({}, config);
64
+    const connectionConfig = Object.assign({}, config);
65
+    const { issuer, jwt } = APP.store.getState()['features/jwt'];
66
 
66
 
67
     connectionConfig.bosh += '?room=' + roomName;
67
     connectionConfig.bosh += '?room=' + roomName;
68
+
68
     let connection
69
     let connection
69
-        = new JitsiMeetJS.JitsiConnection(null, config.token, connectionConfig);
70
+        = new JitsiMeetJS.JitsiConnection(
71
+            null,
72
+            jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
73
+            connectionConfig);
70
 
74
 
71
     return new Promise(function (resolve, reject) {
75
     return new Promise(function (resolve, reject) {
72
         connection.addEventListener(
76
         connection.addEventListener(
73
-            ConnectionEvents.CONNECTION_ESTABLISHED, handleConnectionEstablished
74
-        );
77
+            ConnectionEvents.CONNECTION_ESTABLISHED,
78
+            handleConnectionEstablished);
75
         connection.addEventListener(
79
         connection.addEventListener(
76
-            ConnectionEvents.CONNECTION_FAILED, handleConnectionFailed
77
-        );
80
+            ConnectionEvents.CONNECTION_FAILED,
81
+            handleConnectionFailed);
78
         connection.addEventListener(
82
         connection.addEventListener(
79
-            ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler);
83
+            ConnectionEvents.CONNECTION_FAILED,
84
+            connectionFailedHandler);
80
 
85
 
81
         function connectionFailedHandler(error, errMsg) {
86
         function connectionFailedHandler(error, errMsg) {
82
             APP.store.dispatch(connectionFailed(connection, error, errMsg));
87
             APP.store.dispatch(connectionFailed(connection, error, errMsg));
91
         function unsubscribe() {
96
         function unsubscribe() {
92
             connection.removeEventListener(
97
             connection.removeEventListener(
93
                 ConnectionEvents.CONNECTION_ESTABLISHED,
98
                 ConnectionEvents.CONNECTION_ESTABLISHED,
94
-                handleConnectionEstablished
95
-            );
99
+                handleConnectionEstablished);
96
             connection.removeEventListener(
100
             connection.removeEventListener(
97
                 ConnectionEvents.CONNECTION_FAILED,
101
                 ConnectionEvents.CONNECTION_FAILED,
98
-                handleConnectionFailed
99
-            );
102
+                handleConnectionFailed);
100
         }
103
         }
101
 
104
 
102
         function handleConnectionEstablished() {
105
         function handleConnectionEstablished() {
129
  * @returns {Promise<JitsiConnection>}
132
  * @returns {Promise<JitsiConnection>}
130
  */
133
  */
131
 export function openConnection({id, password, retry, roomName}) {
134
 export function openConnection({id, password, retry, roomName}) {
132
-
133
     let usernameOverride
135
     let usernameOverride
134
         = jitsiLocalStorage.getItem("xmpp_username_override");
136
         = jitsiLocalStorage.getItem("xmpp_username_override");
135
     let passwordOverride
137
     let passwordOverride
138
     if (usernameOverride && usernameOverride.length > 0) {
140
     if (usernameOverride && usernameOverride.length > 0) {
139
         id = usernameOverride;
141
         id = usernameOverride;
140
     }
142
     }
141
-
142
     if (passwordOverride && passwordOverride.length > 0) {
143
     if (passwordOverride && passwordOverride.length > 0) {
143
         password = passwordOverride;
144
         password = passwordOverride;
144
     }
145
     }
145
 
146
 
146
-    return connect(id, password, roomName).catch(function (err) {
147
-        if (!retry) {
148
-            throw err;
149
-        }
147
+    return connect(id, password, roomName).catch(err => {
148
+        if (retry) {
149
+            const { issuer, jwt } = APP.store.getState()['features/jwt'];
150
 
150
 
151
-        if (err === ConnectionErrors.PASSWORD_REQUIRED) {
152
-            // do not retry if token is not valid
153
-            if (config.token) {
154
-                throw err;
155
-            } else {
151
+            if (err === ConnectionErrors.PASSWORD_REQUIRED
152
+                    && (!jwt || issuer === 'anonymous')) {
156
                 return AuthHandler.requestAuth(roomName, connect);
153
                 return AuthHandler.requestAuth(roomName, connect);
157
             }
154
             }
158
-        } else {
159
-            throw err;
160
         }
155
         }
156
+
157
+        throw err;
161
     });
158
     });
162
 }
159
 }

+ 12
- 18
connection_optimization/do_external_connect.js 查看文件

7
 
7
 
8
 /**
8
 /**
9
  * Implements external connect using createConnectionExternally function defined
9
  * Implements external connect using createConnectionExternally function defined
10
- * in external_connect.js for Jitsi Meet. Parses the room name and token from
11
- * the URL and executes createConnectionExternally.
10
+ * in external_connect.js for Jitsi Meet. Parses the room name and JSON Web
11
+ * Token (JWT) from the URL and executes createConnectionExternally.
12
  *
12
  *
13
- * NOTE: If you are using lib-jitsi-meet without Jitsi Meet you should use this
13
+ * NOTE: If you are using lib-jitsi-meet without Jitsi Meet, you should use this
14
  * file as reference only because the implementation is Jitsi Meet-specific.
14
  * file as reference only because the implementation is Jitsi Meet-specific.
15
  *
15
  *
16
  * NOTE: For optimal results this file should be included right after
16
  * NOTE: For optimal results this file should be included right after
17
  * external_connect.js.
17
  * external_connect.js.
18
  */
18
  */
19
 
19
 
20
-const hashParams = parseURLParams(window.location, true);
20
+if (typeof createConnectionExternally === 'function') {
21
+    // URL params have higher proirity than config params.
22
+    let url
23
+        = parseURLParams(window.location, true, 'hash')[
24
+                'config.externalConnectUrl']
25
+            || config.externalConnectUrl;
26
+    let roomName;
21
 
27
 
22
-// URL params have higher proirity than config params.
23
-let url = hashParams['config.externalConnectUrl'] || config.externalConnectUrl;
24
-
25
-if (url && window.createConnectionExternally) {
26
-    const roomName = getRoomName();
27
-
28
-    if (roomName) {
28
+    if (url && (roomName = getRoomName())) {
29
         url += `?room=${roomName}`;
29
         url += `?room=${roomName}`;
30
 
30
 
31
-        let token = hashParams['config.token'] || config.token;
31
+        const token = parseURLParams(window.location, true, 'search').jwt;
32
 
32
 
33
-        if (!token) {
34
-            const searchParams
35
-                = parseURLParams(window.location, true, 'search');
36
-
37
-            token = searchParams.jwt;
38
-        }
39
         if (token) {
33
         if (token) {
40
             url += `&token=${token}`;
34
             url += `&token=${token}`;
41
         }
35
         }

+ 14
- 5
modules/UI/UI.js 查看文件

362
 
362
 
363
     }
363
     }
364
 
364
 
365
-    if(APP.tokenData.callee) {
366
-        UI.showRingOverlay();
367
-    }
365
+    const { callee } = APP.store.getState()['features/jwt'];
366
+
367
+    callee && UI.showRingOverlay();
368
 };
368
 };
369
 
369
 
370
 /**
370
 /**
1332
     = enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
1332
     = enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
1333
 
1333
 
1334
 UI.showRingOverlay = function () {
1334
 UI.showRingOverlay = function () {
1335
-    RingOverlay.show(APP.tokenData.callee, interfaceConfig.DISABLE_RINGING);
1335
+    const { callee } = APP.store.getState()['features/jwt'];
1336
+
1337
+    callee && RingOverlay.show(callee, interfaceConfig.DISABLE_RINGING);
1338
+
1336
     Filmstrip.toggleFilmstrip(false, false);
1339
     Filmstrip.toggleFilmstrip(false, false);
1337
 };
1340
 };
1338
 
1341
 
1397
         UI.toggleContactList
1400
         UI.toggleContactList
1398
     ], [
1401
     ], [
1399
         UIEvents.TOGGLE_PROFILE,
1402
         UIEvents.TOGGLE_PROFILE,
1400
-        () => APP.tokenData.isGuest && UI.toggleSidePanel("profile_container")
1403
+        () => {
1404
+            const {
1405
+                isGuest
1406
+            } = APP.store.getState()['features/jwt'];
1407
+
1408
+            isGuest && UI.toggleSidePanel('profile_container');
1409
+        }
1401
     ], [
1410
     ], [
1402
         UIEvents.TOGGLE_FILMSTRIP,
1411
         UIEvents.TOGGLE_FILMSTRIP,
1403
         UI.handleToggleFilmstrip
1412
         UI.handleToggleFilmstrip

+ 15
- 8
modules/UI/authentication/AuthHandler.js 查看文件

1
 /* global APP, config, JitsiMeetJS, Promise */
1
 /* global APP, config, JitsiMeetJS, Promise */
2
-const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
2
 
4
-import LoginDialog from './LoginDialog';
3
+import { openConnection } from '../../../connection';
4
+import { setJWT } from '../../../react/features/jwt';
5
 import UIUtil from '../util/UIUtil';
5
 import UIUtil from '../util/UIUtil';
6
-import {openConnection} from '../../../connection';
6
+
7
+import LoginDialog from './LoginDialog';
7
 
8
 
8
 const ConnectionErrors = JitsiMeetJS.errors.connection;
9
 const ConnectionErrors = JitsiMeetJS.errors.connection;
10
+const logger = require("jitsi-meet-logger").getLogger(__filename);
9
 
11
 
10
 let externalAuthWindow;
12
 let externalAuthWindow;
11
 let authRequiredDialog;
13
 let authRequiredDialog;
73
  * @param room the name fo the conference room.
75
  * @param room the name fo the conference room.
74
  */
76
  */
75
 function initJWTTokenListener(room) {
77
 function initJWTTokenListener(room) {
76
-    var listener = function (event) {
77
-        if (externalAuthWindow !== event.source) {
78
+    var listener = function ({ data, source }) {
79
+        if (externalAuthWindow !== source) {
78
             logger.warn("Ignored message not coming " +
80
             logger.warn("Ignored message not coming " +
79
                 "from external authnetication window");
81
                 "from external authnetication window");
80
             return;
82
             return;
81
         }
83
         }
82
-        if (event.data && event.data.jwtToken) {
83
-            config.token = event.data.jwtToken;
84
-            logger.info("Received JWT token:", config.token);
84
+
85
+        let jwt;
86
+
87
+        if (data && (jwt = data.jwtToken)) {
88
+            logger.info("Received JSON Web Token (JWT):", jwt);
89
+
90
+            APP.store.dispatch(setJWT(jwt));
91
+
85
             var roomName = room.getName();
92
             var roomName = room.getName();
86
             openConnection({retry: false, roomName: roomName })
93
             openConnection({retry: false, roomName: roomName })
87
                 .then(function (connection) {
94
                 .then(function (connection) {

+ 10
- 6
modules/UI/ring_overlay/RingOverlay.js 查看文件

22
  */
22
  */
23
 class RingOverlay {
23
 class RingOverlay {
24
     /**
24
     /**
25
-     * @param callee instance of User class from TokenData.js
25
+     *
26
+     * @param callee The callee (Object) as defined by the JWT support.
26
      * @param {boolean} disableRingingSound if true the ringing sound wont be played.
27
      * @param {boolean} disableRingingSound if true the ringing sound wont be played.
27
      */
28
      */
28
     constructor(callee, disableRingingSound) {
29
     constructor(callee, disableRingingSound) {
77
             <div id="${this._containerId}" class='ringing' >
78
             <div id="${this._containerId}" class='ringing' >
78
                 <div class='ringing__content'>
79
                 <div class='ringing__content'>
79
                     ${callingLabel}
80
                     ${callingLabel}
80
-                    <img class='ringing__avatar' src="${callee.getAvatarUrl()}" />
81
+                    <img class='ringing__avatar' src="${callee.avatarUrl}" />
81
                     <div class="ringing__caller-info">
82
                     <div class="ringing__caller-info">
82
-                        <p>${callee.getName()}${callerStateLabel}</p>
83
+                        <p>${callee.name}${callerStateLabel}</p>
83
                     </div>
84
                     </div>
84
                 </div>
85
                 </div>
85
                 ${audioHTML}
86
                 ${audioHTML}
137
 export default {
138
 export default {
138
     /**
139
     /**
139
      * Shows the ring overlay for the passed callee.
140
      * Shows the ring overlay for the passed callee.
140
-     * @param callee {class User} the callee. Instance of User class from
141
-     * TokenData.js
142
-     * @param {boolean} disableRingingSound if true the ringing sound wont be played.
141
+     *
142
+     * @param {Object} callee - The callee. Object containing data about
143
+     * callee.
144
+     * @param {boolean} disableRingingSound - If true the ringing sound won't be
145
+     * played.
146
+     * @returns {void}
143
      */
147
      */
144
     show(callee, disableRingingSound = false) {
148
     show(callee, disableRingingSound = false) {
145
         if(overlay) {
149
         if(overlay) {

+ 17
- 11
modules/analytics/analytics.js 查看文件

1
 /* global JitsiMeetJS, config, APP */
1
 /* global JitsiMeetJS, config, APP */
2
+
2
 /**
3
 /**
3
  * Load the integration of a third-party analytics API such as Google
4
  * Load the integration of a third-party analytics API such as Google
4
  * Analytics. Since we cannot guarantee the quality of the third-party service
5
  * Analytics. Since we cannot guarantee the quality of the third-party service
101
      * null.
102
      * null.
102
      */
103
      */
103
     init() {
104
     init() {
104
-        let analytics = JitsiMeetJS.analytics;
105
-        if(!this.isEnabled() || !analytics)
105
+        const { analytics } = JitsiMeetJS;
106
+
107
+        if (!this.isEnabled() || !analytics)
106
             return;
108
             return;
107
 
109
 
108
-        this._loadHandlers()
109
-            .then(handlers => {
110
-                let permanentProperties = {
111
-                    userAgent: navigator.userAgent,
112
-                    roomName: APP.conference.roomName
110
+        this._loadHandlers().then(
111
+            handlers => {
112
+                const permanentProperties = {
113
+                    roomName: APP.conference.roomName,
114
+                    userAgent: navigator.userAgent
113
                 };
115
                 };
114
-                let {server, group} = APP.tokenData;
115
-                if(server) {
116
+
117
+                const { group, server } = APP.store.getState()['features/jwt'];
118
+
119
+                if (server) {
116
                     permanentProperties.server = server;
120
                     permanentProperties.server = server;
117
                 }
121
                 }
118
-                if(group) {
122
+                if (group) {
119
                     permanentProperties.group = group;
123
                     permanentProperties.group = group;
120
                 }
124
                 }
125
+
121
                 analytics.addPermanentProperties(permanentProperties);
126
                 analytics.addPermanentProperties(permanentProperties);
122
                 analytics.setAnalyticsHandlers(handlers);
127
                 analytics.setAnalyticsHandlers(handlers);
123
-            }, error => analytics.dispose() && console.error(error));
128
+            },
129
+            error => analytics.dispose() && console.error(error));
124
 
130
 
125
     }
131
     }
126
 }
132
 }

+ 0
- 123
modules/tokendata/TokenData.js 查看文件

1
-/* global config */
2
-
3
-/**
4
- * Parses and handles JWT tokens. Sets config.token.
5
- */
6
-
7
-import * as jws from "jws";
8
-
9
-import { getConfigParamsFromUrl } from '../../react/features/base/config';
10
-
11
-/**
12
- * Get the JWT token from the URL.
13
- */
14
-let params = getConfigParamsFromUrl(window.location, true, 'search');
15
-let jwt = params.jwt;
16
-
17
-/**
18
- * Implements a user of conference.
19
- */
20
-class User {
21
-    /**
22
-     * @param name {string} the name of the user.
23
-     * @param email {string} the email of the user.
24
-     * @param avatarUrl {string} the URL for the avatar of the user.
25
-     */
26
-    constructor(name, email, avatarUrl) {
27
-        this._name = name;
28
-        this._email = email;
29
-        this._avatarUrl = avatarUrl;
30
-    }
31
-
32
-    /**
33
-     * GETERS START.
34
-     */
35
-
36
-    /**
37
-     * Returns the name property
38
-     */
39
-    getName() {
40
-        return this._name;
41
-    }
42
-
43
-    /**
44
-     * Returns the email property
45
-     */
46
-    getEmail() {
47
-        return this._email;
48
-    }
49
-
50
-    /**
51
-     * Returns the URL of the avatar
52
-     */
53
-    getAvatarUrl() {
54
-        return this._avatarUrl;
55
-    }
56
-
57
-    /**
58
-     * GETERS END.
59
-     */
60
-}
61
-
62
-/**
63
- * Represent the data parsed from the JWT token
64
- */
65
-class TokenData{
66
-    /**
67
-     * @param {string} the JWT token
68
-     */
69
-    constructor(jwt) {
70
-        this.isGuest = true;
71
-        if(!jwt)
72
-            return;
73
-
74
-        this.isGuest = config.enableUserRolesBasedOnToken !== true;
75
-
76
-        this.jwt = jwt;
77
-
78
-        this._decode();
79
-        // Use JWT param as token if there is not other token set and if the
80
-        // iss field is not anonymous. If you want to pass data with JWT token
81
-        // but you don't want to pass the JWT token for verification the iss
82
-        // field should be set to "anonymous"
83
-        if(!config.token && this.payload && this.payload.iss !== "anonymous")
84
-            config.token = jwt;
85
-    }
86
-
87
-    /**
88
-     * Decodes the JWT token and sets the decoded data to properties.
89
-     */
90
-    _decode() {
91
-        this.decodedJWT = jws.decode(jwt);
92
-        if(!this.decodedJWT || !this.decodedJWT.payload)
93
-            return;
94
-        this.payload = this.decodedJWT.payload;
95
-        if(!this.payload.context)
96
-            return;
97
-        this.server = this.payload.context.server;
98
-        this.group = this.payload.context.group;
99
-        let callerData = this.payload.context.user;
100
-        let calleeData = this.payload.context.callee;
101
-        if(callerData)
102
-            this.caller = new User(callerData.name, callerData.email,
103
-                callerData.avatarUrl);
104
-        if(calleeData)
105
-            this.callee = new User(calleeData.name, calleeData.email,
106
-                calleeData.avatarUrl);
107
-    }
108
-}
109
-
110
-/**
111
- * Stores the TokenData instance.
112
- */
113
-let data = null;
114
-
115
-/**
116
- * Returns the data variable. Creates new TokenData instance if <tt>data</tt>
117
- * variable is null.
118
- */
119
-export default function getTokenData() {
120
-    if(!data)
121
-        data = new TokenData(jwt);
122
-    return data;
123
-}

+ 1
- 1
package.json 查看文件

39
     "jQuery-Impromptu": "trentrichardson/jQuery-Impromptu#v6.0.0",
39
     "jQuery-Impromptu": "trentrichardson/jQuery-Impromptu#v6.0.0",
40
     "jquery-ui": "1.10.5",
40
     "jquery-ui": "1.10.5",
41
     "jssha": "1.5.0",
41
     "jssha": "1.5.0",
42
-    "jws": "3.1.4",
42
+    "jwt-decode": "2.2.0",
43
     "lib-jitsi-meet": "jitsi/lib-jitsi-meet",
43
     "lib-jitsi-meet": "jitsi/lib-jitsi-meet",
44
     "lodash": "4.17.4",
44
     "lodash": "4.17.4",
45
     "postis": "2.2.0",
45
     "postis": "2.2.0",

+ 3
- 3
react/features/app/actions.js 查看文件

1
-import { setRoom, setRoomUrl } from '../base/conference';
1
+import { setRoom, setRoomURL } from '../base/conference';
2
 import { setConfig } from '../base/config';
2
 import { setConfig } from '../base/config';
3
 import { getDomain, setDomain } from '../base/connection';
3
 import { getDomain, setDomain } from '../base/connection';
4
 import { loadConfig } from '../base/lib-jitsi-meet';
4
 import { loadConfig } from '../base/lib-jitsi-meet';
18
  * @returns {Function}
18
  * @returns {Function}
19
  */
19
  */
20
 export function appInit() {
20
 export function appInit() {
21
-    return () => init();
21
+    return (dispatch, getState) => init(getState());
22
 }
22
 }
23
 
23
 
24
 /**
24
 /**
55
             urlObject = new URL(urlWithoutDomain, `https://${domain}`);
55
             urlObject = new URL(urlWithoutDomain, `https://${domain}`);
56
         }
56
         }
57
 
57
 
58
-        dispatch(setRoomUrl(urlObject));
58
+        dispatch(setRoomURL(urlObject));
59
 
59
 
60
         // TODO Kostiantyn Tsaregradskyi: We should probably detect if user is
60
         // TODO Kostiantyn Tsaregradskyi: We should probably detect if user is
61
         // currently in a conference and ask her if she wants to close the
61
         // currently in a conference and ask her if she wants to close the

+ 5
- 4
react/features/app/functions.web.js 查看文件

16
 
16
 
17
 import KeyboardShortcut
17
 import KeyboardShortcut
18
     from '../../../modules/keyboardshortcut/keyboardshortcut';
18
     from '../../../modules/keyboardshortcut/keyboardshortcut';
19
-import getTokenData from '../../../modules/tokendata/TokenData';
20
 import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
19
 import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
21
 
20
 
22
 declare var APP: Object;
21
 declare var APP: Object;
111
  * Temporary solution. Later we'll get rid of global APP and set its properties
110
  * Temporary solution. Later we'll get rid of global APP and set its properties
112
  * in redux store.
111
  * in redux store.
113
  *
112
  *
113
+ * @param {Object} state - Snapshot of current state of redux store.
114
  * @returns {void}
114
  * @returns {void}
115
  */
115
  */
116
-export function init() {
116
+export function init(state: Object) {
117
     _initLogging();
117
     _initLogging();
118
 
118
 
119
     APP.keyboardshortcut = KeyboardShortcut;
119
     APP.keyboardshortcut = KeyboardShortcut;
120
-    APP.tokenData = getTokenData();
120
+
121
+    const { jwt } = state['features/jwt'];
121
 
122
 
122
     // Force enable the API if jwt token is passed because most probably
123
     // Force enable the API if jwt token is passed because most probably
123
     // jitsi meet is displayed inside of wrapper that will need to communicate
124
     // jitsi meet is displayed inside of wrapper that will need to communicate
124
     // with jitsi meet.
125
     // with jitsi meet.
125
-    APP.API.init(APP.tokenData.jwt ? { forceEnable: true } : undefined);
126
+    APP.API.init(jwt ? { forceEnable: true } : undefined);
126
 
127
 
127
     APP.translation.init();
128
     APP.translation.init();
128
 }
129
 }

+ 5
- 0
react/features/app/index.js 查看文件

3
 export * from './components';
3
 export * from './components';
4
 export * from './functions';
4
 export * from './functions';
5
 
5
 
6
+// We need to import the jwt module in order to register the reducer and
7
+// middleware, because the module is not used outside of this feature.
8
+import '../jwt';
9
+
6
 import './reducer';
10
 import './reducer';
11
+

+ 26
- 3
react/features/base/react/components/Watermarks.web.js 查看文件

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
+import { connect } from 'react-redux';
4
 
5
 
5
 import { translate } from '../../i18n';
6
 import { translate } from '../../i18n';
6
 
7
 
7
-declare var APP: Object;
8
 declare var interfaceConfig: Object;
8
 declare var interfaceConfig: Object;
9
 
9
 
10
 /**
10
 /**
131
         let reactElement = null;
131
         let reactElement = null;
132
 
132
 
133
         if (this.state.showJitsiWatermark
133
         if (this.state.showJitsiWatermark
134
-                || (APP.tokenData.isGuest
134
+                || (this.props._isGuest
135
                     && this.state.showJitsiWatermarkForGuests)) {
135
                     && this.state.showJitsiWatermarkForGuests)) {
136
             reactElement = <div className = 'watermark leftwatermark' />;
136
             reactElement = <div className = 'watermark leftwatermark' />;
137
 
137
 
175
     }
175
     }
176
 }
176
 }
177
 
177
 
178
-export default translate(Watermarks);
178
+/**
179
+ * Maps parts of Redux store to component prop types.
180
+ *
181
+ * @param {Object} state - Snapshot of Redux store.
182
+ * @returns {{
183
+ *      _isGuest: boolean
184
+ * }}
185
+ */
186
+function _mapStateToProps(state) {
187
+    const { isGuest } = state['features/jwt'];
188
+
189
+    return {
190
+        /**
191
+         * The indicator which determines whether the local participant is a
192
+         * guest in the conference.
193
+         *
194
+         * @private
195
+         * @type {boolean}
196
+         */
197
+        _isGuest: isGuest
198
+    };
199
+}
200
+
201
+export default connect(_mapStateToProps)(translate(Watermarks));

+ 6
- 5
react/features/conference/route.js 查看文件

113
  * @returns {void}
113
  * @returns {void}
114
  */
114
  */
115
 function _setTokenData() {
115
 function _setTokenData() {
116
-    const localUser = APP.tokenData.caller;
116
+    const state = APP.store.getState();
117
+    const { caller } = state['features/jwt'];
117
 
118
 
118
-    if (localUser) {
119
-        const email = localUser.getEmail();
120
-        const avatarUrl = localUser.getAvatarUrl();
121
-        const name = localUser.getName();
119
+    if (caller) {
120
+        const email = caller.email;
121
+        const avatarUrl = caller.avatarUrl;
122
+        const name = caller.name;
122
 
123
 
123
         APP.settings.setEmail((email || '').trim(), true);
124
         APP.settings.setEmail((email || '').trim(), true);
124
         APP.settings.setAvatarUrl((avatarUrl || '').trim());
125
         APP.settings.setAvatarUrl((avatarUrl || '').trim());

+ 12
- 0
react/features/jwt/actionTypes.js 查看文件

1
+import { Symbol } from '../base/react';
2
+
3
+/**
4
+ * The type of redux action which stores a specific JSON Web Token (JWT) into
5
+ * the redux store.
6
+ *
7
+ * {
8
+ *     type: SET_JWT,
9
+ *     jwt: string
10
+ * }
11
+ */
12
+export const SET_JWT = Symbol('SET_JWT');

+ 19
- 0
react/features/jwt/actions.js 查看文件

1
+/* @flow */
2
+
3
+import { SET_JWT } from './actionTypes';
4
+
5
+/**
6
+ * Stores a specific JSON Web Token (JWT) into the redux store.
7
+ *
8
+ * @param {string} jwt - The JSON Web Token (JWT) to store.
9
+ * @returns {{
10
+ *     type: SET_TOKEN_DATA,
11
+ *     jwt: string
12
+ * }}
13
+ */
14
+export function setJWT(jwt: string) {
15
+    return {
16
+        type: SET_JWT,
17
+        jwt
18
+    };
19
+}

+ 4
- 0
react/features/jwt/index.js 查看文件

1
+export * from './actions';
2
+
3
+import './middleware';
4
+import './reducer';

+ 106
- 0
react/features/jwt/middleware.js 查看文件

1
+import jwtDecode from 'jwt-decode';
2
+
3
+import { SET_ROOM_URL } from '../base/conference';
4
+import { parseURLParams, SET_CONFIG } from '../base/config';
5
+import { MiddlewareRegistry } from '../base/redux';
6
+
7
+import { setJWT } from './actions';
8
+import { SET_JWT } from './actionTypes';
9
+
10
+/**
11
+ * Middleware to parse token data upon setting a new room URL.
12
+ *
13
+ * @param {Store} store - The Redux store.
14
+ * @private
15
+ * @returns {Function}
16
+ */
17
+MiddlewareRegistry.register(store => next => action => {
18
+    switch (action.type) {
19
+    case SET_CONFIG:
20
+    case SET_ROOM_URL:
21
+        // XXX The JSON Web Token (JWT) is not the only piece of state that we
22
+        // have decided to store in the feature jwt, there is isGuest as well
23
+        // which depends on the states of the features base/config and jwt. So
24
+        // the JSON Web Token comes from the room's URL and isGuest needs a
25
+        // recalculation upon SET_CONFIG as well.
26
+        return _setConfigOrRoomURL(store, next, action);
27
+
28
+    case SET_JWT:
29
+        return _setJWT(store, next, action);
30
+    }
31
+
32
+    return next(action);
33
+});
34
+
35
+/**
36
+ * Notifies the feature jwt that the action {@link SET_CONFIG} or
37
+ * {@link SET_ROOM_URL} is being dispatched within a specific Redux
38
+ * {@code store}.
39
+ *
40
+ * @param {Store} store - The Redux store in which the specified {@code action}
41
+ * is being dispatched.
42
+ * @param {Dispatch} next - The Redux dispatch function to dispatch the
43
+ * specified {@code action} to the specified {@code store}.
44
+ * @param {Action} action - The Redux action {@code SET_CONFIG} or
45
+ * {@code SET_ROOM_NAME} which is being dispatched in the specified
46
+ * {@code store}.
47
+ * @private
48
+ * @returns {Object} The new state that is the result of the reduction of the
49
+ * specified {@code action}.
50
+ */
51
+function _setConfigOrRoomURL({ dispatch, getState }, next, action) {
52
+    const result = next(action);
53
+
54
+    const { roomURL } = getState()['features/base/conference'];
55
+    let jwt;
56
+
57
+    if (roomURL) {
58
+        jwt = parseURLParams(roomURL, true, 'search').jwt;
59
+    }
60
+    dispatch(setJWT(jwt));
61
+
62
+    return result;
63
+}
64
+
65
+/**
66
+ * Notifies the feature jwt that the action {@link SET_JWT} is being dispatched
67
+ * within a specific Redux {@code store}.
68
+ *
69
+ * @param {Store} store - The Redux store in which the specified {@code action}
70
+ * is being dispatched.
71
+ * @param {Dispatch} next - The Redux dispatch function to dispatch the
72
+ * specified {@code action} to the specified {@code store}.
73
+ * @param {Action} action - The Redux action {@code SET_JWT} which is being
74
+ * dispatched in the specified {@code store}.
75
+ * @private
76
+ * @returns {Object} The new state that is the result of the reduction of the
77
+ * specified {@code action}.
78
+ */
79
+function _setJWT({ getState }, next, action) {
80
+    // eslint-disable-next-line no-unused-vars
81
+    const { jwt, type, ...actionPayload } = action;
82
+
83
+    if (jwt && !Object.keys(actionPayload).length) {
84
+        const {
85
+            enableUserRolesBasedOnToken
86
+        } = getState()['features/base/config'];
87
+
88
+        action.isGuest = !enableUserRolesBasedOnToken;
89
+
90
+        const jwtPayload = jwtDecode(jwt);
91
+
92
+        if (jwtPayload) {
93
+            const { context, iss } = jwtPayload;
94
+
95
+            action.issuer = iss;
96
+            if (context) {
97
+                action.callee = context.callee;
98
+                action.caller = context.user;
99
+                action.group = context.group;
100
+                action.server = context.server;
101
+            }
102
+        }
103
+    }
104
+
105
+    return next(action);
106
+}

+ 47
- 0
react/features/jwt/reducer.js 查看文件

1
+import { equals, ReducerRegistry } from '../base/redux';
2
+
3
+import { SET_JWT } from './actionTypes';
4
+
5
+/**
6
+ * The initial redux state of the feature jwt.
7
+ *
8
+ * @private
9
+ * @type {{
10
+ *     isGuest: boolean
11
+ * }}
12
+ */
13
+const _INITIAL_STATE = {
14
+    /**
15
+     * The indicator which determines whether the local participant is a guest
16
+     * in the conference.
17
+     *
18
+     * @type {boolean}
19
+     */
20
+    isGuest: true
21
+};
22
+
23
+/**
24
+ * Reduces redux actions which affect the JSON Web Token (JWT) stored in the
25
+ * redux store.
26
+ *
27
+ * @param {Object} state - The current redux state.
28
+ * @param {Object} action - The redux action to reduce.
29
+ * @returns {Object} The next redux state which is the result of reducing the
30
+ * specified {@code action}.
31
+ */
32
+ReducerRegistry.register('features/jwt', (state = _INITIAL_STATE, action) => {
33
+    switch (action.type) {
34
+    case SET_JWT: {
35
+        // eslint-disable-next-line no-unused-vars
36
+        const { type, ...payload } = action;
37
+        const nextState = {
38
+            ..._INITIAL_STATE,
39
+            ...payload
40
+        };
41
+
42
+        return equals(state, nextState) ? state : nextState;
43
+    }
44
+    }
45
+
46
+    return state;
47
+});

+ 28
- 8
react/features/toolbox/components/SecondaryToolbar.web.js 查看文件

33
      * @static
33
      * @static
34
      */
34
      */
35
     static propTypes = {
35
     static propTypes = {
36
+        /**
37
+         * The indicator which determines whether the local participant is a
38
+         * guest in the conference.
39
+         */
40
+        _isGuest: React.PropTypes.bool,
41
+
36
         /**
42
         /**
37
          * Handler dispatching local "Raise hand".
43
          * Handler dispatching local "Raise hand".
38
          */
44
          */
79
              * @type {Object}
85
              * @type {Object}
80
              */
86
              */
81
             profile: {
87
             profile: {
82
-                onMount: () =>
83
-                    APP.tokenData.isGuest
84
-                        || this.props._onSetProfileButtonUnclickable(true)
88
+                onMount: () => {
89
+                    const {
90
+                        _isGuest,
91
+                        _onSetProfileButtonUnclickable
92
+                    } = this.props;
93
+
94
+                    _isGuest || _onSetProfileButtonUnclickable(true);
95
+                }
85
             },
96
             },
86
 
97
 
87
             /**
98
             /**
237
  *
248
  *
238
  * @param {Object} state - Snapshot of Redux store.
249
  * @param {Object} state - Snapshot of Redux store.
239
  * @returns {{
250
  * @returns {{
251
+ *     _isGuest: boolean,
240
  *     _secondaryToolbarButtons: Map,
252
  *     _secondaryToolbarButtons: Map,
241
  *     _visible: boolean
253
  *     _visible: boolean
242
  * }}
254
  * }}
243
  * @private
255
  * @private
244
  */
256
  */
245
 function _mapStateToProps(state: Object): Object {
257
 function _mapStateToProps(state: Object): Object {
246
-    const {
247
-        secondaryToolbarButtons,
248
-        visible
249
-    } = state['features/toolbox'];
258
+    const { isGuest } = state['features/jwt'];
259
+    const { secondaryToolbarButtons, visible } = state['features/toolbox'];
250
 
260
 
251
     return {
261
     return {
262
+        /**
263
+         * The indicator which determines whether the local participant is a
264
+         * guest in the conference.
265
+         *
266
+         * @private
267
+         * @type {boolean}
268
+         */
269
+        _isGuest: isGuest,
270
+
252
         /**
271
         /**
253
          * Default toolbar buttons for secondary toolbar.
272
          * Default toolbar buttons for secondary toolbar.
254
          *
273
          *
258
         _secondaryToolbarButtons: secondaryToolbarButtons,
277
         _secondaryToolbarButtons: secondaryToolbarButtons,
259
 
278
 
260
         /**
279
         /**
261
-         * Shows whether toolbar is visible.
280
+         * The indicator which determines whether the {@code SecondaryToolbar}
281
+         * is visible.
262
          *
282
          *
263
          * @private
283
          * @private
264
          * @type {boolean}
284
          * @type {boolean}

正在加载...
取消
保存