Browse Source

XMPP authentication improvements. Makes it possible to authenticate during the conference.

j8
paweldomas 10 years ago
parent
commit
588c2d9e4b

+ 17
- 0
lang/main.json View File

@@ -132,6 +132,7 @@
132 132
         "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.",
133 133
         "joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.",
134 134
         "connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
135
+        "connecting": "Connecting",
135 136
         "error": "Error",
136 137
         "detectext": "Error when trying to detect desktopsharing extension.",
137 138
         "failtoinstall": "Failed to install desktop sharing extension",
@@ -159,6 +160,7 @@
159 160
         "AuthMsg": "Authentication is required to create room:<br/><b>__room__ </b></br> You can either authenticate to create the room or just wait for someone else to do so.",
160 161
         "Authenticate": "Authenticate",
161 162
         "Cancel": "Cancel",
163
+        "retry": "Retry",
162 164
         "logoutTitle" : "Logout",
163 165
         "logoutQuestion" : "Are you sure you want to logout and stop the conference?",
164 166
         "sessTerminated": "Session Terminated",
@@ -216,5 +218,20 @@
216 218
             "Talk to you in a sec!"
217 219
         ],
218 220
         "and": "and"
221
+    },
222
+    "connection":
223
+    {
224
+        "ERROR": "Error",
225
+        "CONNECTING": "Connecting",
226
+        "CONNFAIL": "Connection failed",
227
+        "AUTHENTICATING": "Authenticating",
228
+        "AUTHFAIL": "Authentication failed",
229
+        "CONNECTED": "Connected",
230
+        "DISCONNECTED": "Disconnected",
231
+        "DISCONNECTING": "Disconnecting",
232
+        "ATTACHED": "Attached",
233
+        "FETCH_SESSION_ID": "Obtaining session-id...",
234
+        "GOT_SESSION_ID": "Obtaining session-id... Done",
235
+        "GET_SESSION_ID_ERROR": "Get session-id error: "
219 236
     }
220 237
 }

+ 37
- 6
modules/UI/authentication/Authentication.js View File

@@ -1,4 +1,7 @@
1
-/* global $, APP */
1
+/* global $, APP*/
2
+
3
+var LoginDialog = require('./LoginDialog');
4
+var Moderator = require('../../xmpp/moderator');
2 5
 
3 6
 /* Initial "authentication required" dialog */
4 7
 var authDialog = null;
@@ -50,6 +53,37 @@ var Authentication = {
50 53
             authenticationWindow = null;
51 54
         }
52 55
     },
56
+    xmppAuthenticate: function () {
57
+
58
+        var loginDialog = LoginDialog.show(
59
+            function (connection, state) {
60
+                if (!state) {
61
+                    // User cancelled
62
+                    loginDialog.close();
63
+                    return;
64
+                } else if (state == APP.xmpp.Status.CONNECTED) {
65
+
66
+                    loginDialog.close();
67
+
68
+                    Authentication.stopInterval();
69
+                    Authentication.closeAuthenticationDialog();
70
+
71
+                    // Close the connection as anonymous one will be used
72
+                    // to create the conference. Session-id will authorize
73
+                    // the request.
74
+                    connection.disconnect();
75
+
76
+                    var roomName = APP.UI.generateRoomName();
77
+                    Moderator.allocateConferenceFocus(roomName, function () {
78
+                        // If it's not "on the fly" authentication now join
79
+                        // the conference room
80
+                        if (!APP.xmpp.getMUCJoined()) {
81
+                            APP.UI.checkForNicknameAndJoin();
82
+                        }
83
+                    });
84
+                }
85
+            }, true);
86
+    },
53 87
     focusAuthenticationWindow: function () {
54 88
         // If auth window exists just bring it to the front
55 89
         if (authenticationWindow) {
@@ -60,7 +94,7 @@ var Authentication = {
60 94
     closeAuthenticationDialog: function () {
61 95
         // Close authentication dialog if opened
62 96
         if (authDialog) {
63
-            APP.UI.messageHandler.closeDialog();
97
+            authDialog.close();
64 98
             authDialog = null;
65 99
         }
66 100
     },
@@ -70,10 +104,7 @@ var Authentication = {
70 104
             // On closed
71 105
             function () {
72 106
                 // Close authentication dialog if opened
73
-                if (authDialog) {
74
-                    messageHandler.closeDialog();
75
-                    authDialog = null;
76
-                }
107
+                Authentication.closeAuthenticationDialog();
77 108
                 callback();
78 109
                 authenticationWindow = null;
79 110
             });

+ 228
- 0
modules/UI/authentication/LoginDialog.js View File

@@ -0,0 +1,228 @@
1
+/* global $, APP, config*/
2
+
3
+var XMPP = require('../../xmpp/xmpp');
4
+var Moderator = require('../../xmpp/moderator');
5
+
6
+//FIXME: use LoginDialog to add retries to XMPP.connect method used when
7
+// anonymous domain is not enabled
8
+
9
+/**
10
+ * Creates new <tt>Dialog</tt> instance.
11
+ * @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt> called
12
+ *        when we either fail to connect or succeed(check Strophe.Status).
13
+ * @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to Jicofo
14
+ *        in order to create session-id after the connection is established.
15
+ * @constructor
16
+ */
17
+function Dialog(callback, obtainSession) {
18
+
19
+    var self = this;
20
+
21
+    var stop = false;
22
+
23
+    var connection = APP.xmpp.createConnection();
24
+
25
+    var message = '<h2 data-i18n="dialog.passwordRequired">';
26
+    message += APP.translation.translateString("dialog.passwordRequired");
27
+    message += '</h2>' +
28
+        '<input name="username" type="text" ' +
29
+        'placeholder="user@domain.net" autofocus>' +
30
+        '<input name="password" ' +
31
+        'type="password" data-i18n="[placeholder]dialog.userPassword"' +
32
+        ' placeholder="user password">';
33
+
34
+    var okButton = APP.translation.generateTranslatonHTML("dialog.Ok");
35
+
36
+    var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel");
37
+
38
+    var states = {
39
+        login: {
40
+            html: message,
41
+            buttons: [
42
+                { title: okButton, value: true},
43
+                { title: cancelButton, value: false}
44
+            ],
45
+            focus: ':input:first',
46
+            submit: function (e, v, m, f) {
47
+                e.preventDefault();
48
+                if (v) {
49
+                    var jid = f.username;
50
+                    var password = f.password;
51
+                    if (jid && password) {
52
+                        stop = false;
53
+                        connection.reset();
54
+                        connDialog.goToState('connecting');
55
+                        connection.connect(jid, password, stateHandler);
56
+                    }
57
+                } else {
58
+                    // User cancelled
59
+                    stop = true;
60
+                    callback();
61
+                }
62
+            }
63
+        },
64
+        connecting: {
65
+            title: APP.translation.translateString('dialog.connecting'),
66
+            html:   '<div id="connectionStatus"></div>',
67
+            buttons: [],
68
+            defaultButton: 0
69
+        },
70
+        finished: {
71
+            title: APP.translation.translateString('dialog.error'),
72
+            html:   '<div id="errorMessage"></div>',
73
+            buttons: [
74
+                {
75
+                    title: APP.translation.translateString('dialog.retry'),
76
+                    value: 'retry'
77
+                },
78
+                {
79
+                    title: APP.translation.translateString('dialog.Cancel'),
80
+                    value: 'cancel'
81
+                },
82
+            ],
83
+            defaultButton: 0,
84
+            submit: function (e, v, m, f) {
85
+                e.preventDefault();
86
+                if (v === 'retry')
87
+                    connDialog.goToState('login');
88
+                else
89
+                    callback();
90
+            }
91
+        }
92
+    };
93
+
94
+    var connDialog
95
+        = APP.UI.messageHandler.openDialogWithStates(states,
96
+                { persistent: true, closeText: '' }, null);
97
+
98
+    var stateHandler = function (status, message) {
99
+        if (stop) {
100
+            return;
101
+        }
102
+
103
+        var translateKey = "connection." + XMPP.getStatusString(status);
104
+        var statusStr = APP.translation.translateString(translateKey);
105
+
106
+        // Display current state
107
+        var connectionStatus =
108
+            connDialog.getState('connecting').find('#connectionStatus');
109
+
110
+        connectionStatus.text(statusStr);
111
+
112
+        switch (status) {
113
+            case XMPP.Status.CONNECTED:
114
+
115
+                stop = true;
116
+                if (!obtainSession) {
117
+                    callback(connection, status);
118
+                    return;
119
+                }
120
+                // Obtaining session-id status
121
+                connectionStatus.text(
122
+                    APP.translation.translateString(
123
+                        'connection.FETCH_SESSION_ID'));
124
+
125
+                // Authenticate with Jicofo and obtain session-id
126
+                var roomName = APP.UI.generateRoomName();
127
+
128
+                // Jicofo will return new session-id when connected
129
+                // from authenticated domain
130
+                connection.sendIQ(
131
+                    Moderator.createConferenceIq(roomName),
132
+                    function (result) {
133
+
134
+                        connectionStatus.text(
135
+                            APP.translation.translateString(
136
+                                'connection.GOT_SESSION_ID'));
137
+
138
+                        stop = true;
139
+
140
+                        // Parse session-id
141
+                        Moderator.parseSessionId(result);
142
+
143
+                        callback(connection, status);
144
+                    },
145
+                    function (error) {
146
+                        console.error("Auth on the fly failed", error);
147
+
148
+                        stop = true;
149
+
150
+                        var errorMsg =
151
+                            APP.translation.translateString(
152
+                                'connection.GET_SESSION_ID_ERROR') +
153
+                                $(error).find('>error').attr('code');
154
+
155
+                        self.displayError(errorMsg);
156
+
157
+                        connection.disconnect();
158
+                    });
159
+
160
+                break;
161
+            case XMPP.Status.AUTHFAIL:
162
+            case XMPP.Status.CONNFAIL:
163
+            case XMPP.Status.DISCONNECTED:
164
+
165
+                stop = true;
166
+
167
+                callback(connection, status);
168
+
169
+                var errorMessage = statusStr;
170
+
171
+                if (message)
172
+                {
173
+                    errorMessage += ': ' + message;
174
+                }
175
+                self.displayError(errorMessage);
176
+
177
+                break;
178
+            default:
179
+                break;
180
+        }
181
+    };
182
+
183
+    /**
184
+     * Displays error message in 'finished' state which allows either to cancel
185
+     * or retry.
186
+     * @param message the final message to be displayed.
187
+     */
188
+    this.displayError = function (message) {
189
+
190
+        var finishedState = connDialog.getState('finished');
191
+
192
+        var errorMessageElem = finishedState.find('#errorMessage');
193
+        errorMessageElem.text(message);
194
+
195
+        connDialog.goToState('finished');
196
+    };
197
+
198
+    /**
199
+     * Closes LoginDialog.
200
+     */
201
+    this.close = function () {
202
+        stop = true;
203
+        connDialog.close();
204
+    };
205
+}
206
+
207
+var LoginDialog = {
208
+
209
+    /**
210
+     * Displays login prompt used to establish new XMPP connection. Given
211
+     * <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
212
+     * called when we connect successfully(status === CONNECTED) or when we fail
213
+     * to do so. On connection failure program can call Dialog.close() method in
214
+     * order to cancel or do nothing to let the user retry.
215
+     * @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
216
+     *        called when we either fail to connect or succeed(check
217
+     *        Strophe.Status).
218
+     * @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
219
+     *        Jicofo in order to create session-id after the connection is
220
+     *        established.
221
+     * @returns {Dialog}
222
+     */
223
+    show: function (callback, obtainSession) {
224
+        return new Dialog(callback, obtainSession);
225
+    }
226
+};
227
+
228
+module.exports = LoginDialog;

+ 4
- 6
modules/UI/toolbars/Toolbar.js View File

@@ -264,12 +264,6 @@ var Toolbar = (function (my) {
264 264
                     loggedIn = true;
265 265
                 }
266 266
 
267
-                //FIXME: XMPP authentication need improvements for "live" login
268
-                if (!APP.xmpp.isExternalAuthEnabled() && !loggedIn)
269
-                {
270
-                    authenticationEnabled = false;
271
-                }
272
-
273 267
                 Toolbar.showAuthenticateButton(authenticationEnabled);
274 268
 
275 269
                 if (authenticationEnabled) {
@@ -292,6 +286,10 @@ var Toolbar = (function (my) {
292 286
 
293 287
     my.authenticateClicked = function () {
294 288
         Authentication.focusAuthenticationWindow();
289
+        if (!APP.xmpp.isExternalAuthEnabled()) {
290
+            Authentication.xmppAuthenticate();
291
+            return;
292
+        }
295 293
         // Get authentication URL
296 294
         if (!APP.xmpp.getMUCJoined()) {
297 295
             APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {

+ 3
- 9
modules/UI/util/MessageHandler.js View File

@@ -94,7 +94,7 @@ var messageHandler = (function(my) {
94 94
         if (persistent) {
95 95
             args.closeText = '';
96 96
         }
97
-        return $.prompt(msgString, args);
97
+        return new Impromptu(msgString, args);
98 98
     };
99 99
 
100 100
     /**
@@ -108,16 +108,10 @@ var messageHandler = (function(my) {
108 108
      * Shows a dialog with different states to the user.
109 109
      *
110 110
      * @param statesObject object containing all the states of the dialog
111
-     * @param loadedFunction function to be called after the prompt is fully loaded
112
-     * @param stateChangedFunction function to be called when the state of the dialog is changed
113 111
      */
114
-    my.openDialogWithStates = function(statesObject, loadedFunction, stateChangedFunction) {
115
-
116
-
117
-        var myPrompt = $.prompt(statesObject);
112
+    my.openDialogWithStates = function (statesObject, options) {
118 113
 
119
-        myPrompt.on('impromptu:loaded', loadedFunction);
120
-        myPrompt.on('impromptu:statechanged', stateChangedFunction);
114
+        return new Impromptu(statesObject, options);
121 115
     };
122 116
 
123 117
     /**

+ 15
- 22
modules/xmpp/moderator.js View File

@@ -188,6 +188,14 @@ var Moderator = {
188 188
         return elem;
189 189
     },
190 190
 
191
+    parseSessionId: function (resultIq) {
192
+        var sessionId = $(resultIq).find('conference').attr('session-id');
193
+        if (sessionId) {
194
+            console.info('Received sessionId: ' + sessionId);
195
+            localStorage.setItem('sessionId', sessionId);
196
+        }
197
+    },
198
+
191 199
     parseConfigOptions: function (resultIq) {
192 200
 
193 201
         Moderator.setFocusUserJid(
@@ -209,12 +217,7 @@ var Moderator = {
209 217
 
210 218
         if (!externalAuthEnabled) {
211 219
             // We expect to receive sessionId in 'internal' authentication mode
212
-            var sessionId
213
-                = $(resultIq).find('conference').attr('session-id');
214
-            if (sessionId) {
215
-                console.info('Received sessionId: ' + sessionId);
216
-                localStorage.setItem('sessionId', sessionId);
217
-            }
220
+            Moderator.parseSessionId(resultIq);
218 221
         }
219 222
 
220 223
         var authIdentity = $(resultIq).find('>conference').attr('identity');
@@ -295,22 +298,12 @@ var Moderator = {
295 298
                 // Not authorized to create new room
296 299
                 if ($(error).find('>error>not-authorized').length) {
297 300
                     console.warn("Unauthorized to start the conference", error);
298
-                    var toDomain
299
-                        = Strophe.getDomainFromJid(error.getAttribute('to'));
300
-                    if (toDomain === config.hosts.anonymousdomain) {
301
-                        // we are connected with anonymous domain and
302
-                        // only non anonymous users can create rooms
303
-                        // we must authorize the user
304
-                        self.xmppService.promptLogin();
305
-                    } else {
306
-                        // External authentication mode
307
-                        eventEmitter.emit(
308
-                            XMPPEvents.AUTHENTICATION_REQUIRED,
309
-                            function () {
310
-                                Moderator.allocateConferenceFocus(
311
-                                    roomName, callback);
312
-                            });
313
-                    }
301
+                    eventEmitter.emit(
302
+                        XMPPEvents.AUTHENTICATION_REQUIRED,
303
+                        function () {
304
+                            Moderator.allocateConferenceFocus(
305
+                                roomName, callback);
306
+                        });
314 307
                     return;
315 308
                 }
316 309
                 var waitMs = getNextErrorTimeout();

+ 16
- 2
modules/xmpp/xmpp.js View File

@@ -13,8 +13,7 @@ var connection = null;
13 13
 var authenticatedUser = false;
14 14
 
15 15
 function connect(jid, password) {
16
-    var bosh = config.bosh || '/http-bind';
17
-    connection = new Strophe.Connection(bosh);
16
+    connection = XMPP.createConnection();
18 17
     Moderator.setConnection(connection);
19 18
 
20 19
     if (connection.disco) {
@@ -127,6 +126,12 @@ function setupEvents() {
127 126
 
128 127
 var XMPP = {
129 128
     sessionTerminated: false,
129
+
130
+    /**
131
+     * XMPP connection status
132
+     */
133
+    Status: Strophe.Status,
134
+
130 135
     /**
131 136
      * Remembers if we were muted by the focus.
132 137
      * @type {boolean}
@@ -146,7 +151,16 @@ var XMPP = {
146 151
         var jid = configDomain || window.location.hostname;
147 152
         connect(jid, null);
148 153
     },
154
+    createConnection: function () {
155
+        var bosh = config.bosh || '/http-bind';
156
+
157
+        return new Strophe.Connection(bosh);
158
+    },
159
+    getStatusString: function (status) {
160
+        return Strophe.getStatusString(status);
161
+    },
149 162
     promptLogin: function () {
163
+        // FIXME: re-use LoginDialog which supports retries
150 164
         APP.UI.showLoginPopup(connect);
151 165
     },
152 166
     joinRoom: function(roomName, useNicks, nick)

Loading…
Cancel
Save