瀏覽代碼

handle external auth; xmpp auth tbd

master
isymchych 9 年之前
父節點
當前提交
58d1c76ab0
共有 7 個檔案被更改,包括 429 行新增172 行删除
  1. 79
    73
      app.js
  2. 119
    42
      lib-jitsi-meet.js
  3. 100
    0
      modules/AuthHandler.js
  4. 4
    0
      modules/UI/UI.js
  5. 39
    56
      modules/UI/authentication/LoginDialog.js
  6. 5
    1
      modules/UI/util/UIUtil.js
  7. 83
    0
      modules/connection.js

+ 79
- 73
app.js 查看文件

@@ -17,6 +17,11 @@ import URLProcessor from "./modules/config/URLProcessor";
17 17
 import RoomnameGenerator from './modules/util/RoomnameGenerator';
18 18
 import CQEvents from './service/connectionquality/CQEvents';
19 19
 import UIEvents from './service/UI/UIEvents';
20
+import LoginDialog from './modules/UI/authentication/LoginDialog';
21
+import UIUtil from './modules/UI/util/UIUtil';
22
+
23
+import {openConnection} from './modules/connection';
24
+import AuthHandler from './modules/AuthHandler';
20 25
 
21 26
 import createRoomLocker from './modules/RoomLocker';
22 27
 
@@ -42,7 +47,7 @@ function buildRoomName () {
42 47
          location ~ ^/([a-zA-Z0-9]+)$ {
43 48
          rewrite ^/(.*)$ / break;
44 49
          }
45
-         */
50
+        */
46 51
         if (path.length > 1) {
47 52
             roomName = path.substr(1).toLowerCase();
48 53
         } else {
@@ -104,62 +109,9 @@ const APP = {
104 109
 
105 110
 const ConnectionEvents = JitsiMeetJS.events.connection;
106 111
 const ConnectionErrors = JitsiMeetJS.errors.connection;
107
-function connect() {
108
-    let connection = new JitsiMeetJS.JitsiConnection(null, null, {
109
-        hosts: config.hosts,
110
-        bosh: config.bosh,
111
-        clientNode: config.clientNode
112
-    });
113 112
 
114
-    return new Promise(function (resolve, reject) {
115
-        let handlers = {};
116
-
117
-        function unsubscribe () {
118
-            Object.keys(handlers).forEach(function (event) {
119
-                connection.removeEventListener(event, handlers[event]);
120
-            });
121
-        }
122
-
123
-        handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
124
-            console.log('CONNECTED');
125
-            unsubscribe();
126
-            resolve(connection);
127
-        };
128
-
129
-        function listenForFailure (event) {
130
-            handlers[event] = function (...args) {
131
-                console.error(`CONNECTION FAILED: ${event}`, ...args);
132
-
133
-                unsubscribe();
134
-                reject([event, ...args]);
135
-            };
136
-        }
137
-
138
-        listenForFailure(ConnectionEvents.CONNECTION_FAILED);
139
-        listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
140
-        listenForFailure(ConnectionErrors.CONNECTION_ERROR);
141
-        listenForFailure(ConnectionErrors.OTHER_ERRORS);
142
-
143
-        // install event listeners
144
-        Object.keys(handlers).forEach(function (event) {
145
-            connection.addEventListener(event, handlers[event]);
146
-        });
147
-
148
-        connection.connect();
149
-    }).catch(function (err) {
150
-        if (err[0] === ConnectionErrors.PASSWORD_REQUIRED) {
151
-            // FIXME ask for password and try again
152
-            return connect();
153
-        }
154
-        console.error('FAILED TO CONNECT', err);
155
-        APP.UI.notifyConnectionFailed(err[1]);
156
-
157
-        throw new Error(err[0]);
158
-    });
159
-}
160
-
161
-var ConferenceEvents = JitsiMeetJS.events.conference;
162
-var ConferenceErrors = JitsiMeetJS.errors.conference;
113
+const ConferenceEvents = JitsiMeetJS.events.conference;
114
+const ConferenceErrors = JitsiMeetJS.errors.conference;
163 115
 function initConference(localTracks, connection) {
164 116
     let room = connection.initJitsiConference(APP.conference.roomName, {
165 117
         openSctp: config.openSctp,
@@ -378,10 +330,6 @@ function initConference(localTracks, connection) {
378 330
         APP.UI.changeDisplayName(APP.conference.localId, nickname);
379 331
     });
380 332
 
381
-    room.on(ConferenceErrors.CONNECTION_ERROR, function () {
382
-        // FIXME handle
383
-    });
384
-
385 333
     APP.UI.addListener(
386 334
         UIEvents.START_MUTED_CHANGED,
387 335
         function (startAudioMuted, startVideoMuted) {
@@ -461,7 +409,7 @@ function initConference(localTracks, connection) {
461 409
     });
462 410
 
463 411
     APP.UI.addListener(UIEvents.AUTH_CLICKED, function () {
464
-        // FIXME handle
412
+        AuthHandler.authenticate(room);
465 413
     });
466 414
 
467 415
     APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) {
@@ -477,22 +425,67 @@ function initConference(localTracks, connection) {
477 425
     });
478 426
 
479 427
     return new Promise(function (resolve, reject) {
480
-        room.on(ConferenceEvents.CONFERENCE_JOINED, resolve);
428
+        room.on(ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined);
429
+        room.on(ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed);
481 430
 
482
-        room.on(ConferenceErrors.PASSWORD_REQUIRED, function () {
483
-            APP.UI.markRoomLocked(true);
484
-            roomLocker.requirePassword().then(function () {
485
-                room.join(roomLocker.password);
486
-            });
487
-        });
431
+        let password;
432
+        let reconnectTimeout;
488 433
 
489
-        // FIXME handle errors here
434
+        function unsubscribe() {
435
+            room.off(
436
+                ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined
437
+            );
438
+            room.off(
439
+                ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed
440
+            );
441
+            if (reconnectTimeout) {
442
+                clearTimeout(reconnectTimeout);
443
+            }
444
+            AuthHandler.closeAuth();
445
+        }
490 446
 
491
-        room.join();
492
-    }).catch(function (err) {
493
-        // FIXME notify that we cannot conenct to the room
447
+        function handleConferenceJoined() {
448
+            unsubscribe();
449
+            resolve();
450
+        }
494 451
 
495
-        throw new Error(err[0]);
452
+        function handleConferenceFailed(err) {
453
+            unsubscribe();
454
+            reject(err);
455
+        }
456
+
457
+        function onConferenceFailed(err, msg = '') {
458
+            console.error('CONFERENCE FAILED:', err, msg);
459
+            switch (err) {
460
+                // room is locked by the password
461
+            case ConferenceErrors.PASSWORD_REQUIRED:
462
+                APP.UI.markRoomLocked(true);
463
+                roomLocker.requirePassword().then(function () {
464
+                    room.join(roomLocker.password);
465
+                });
466
+                break;
467
+
468
+            case ConferenceErrors.CONNECTION_ERROR:
469
+                APP.UI.notifyConnectionFailed(msg);
470
+                break;
471
+
472
+                // not enough rights to create conference
473
+            case ConferenceErrors.AUTHENTICATION_REQUIRED:
474
+                // schedule reconnect to check if someone else created the room
475
+                reconnectTimeout = setTimeout(function () {
476
+                    room.join(password);
477
+                }, 5000);
478
+
479
+                // notify user that auth is required
480
+                AuthHandler.requireAuth(APP.conference.roomName);
481
+                break;
482
+
483
+            default:
484
+                handleConferenceFailed(err);
485
+            }
486
+        }
487
+
488
+        room.join(password);
496 489
     });
497 490
 }
498 491
 
@@ -505,9 +498,22 @@ function createLocalTracks () {
505 498
     });
506 499
 }
507 500
 
501
+function connect() {
502
+    return openConnection({retry: true}).catch(function (err) {
503
+        if (err === ConnectionErrors.PASSWORD_REQUIRED) {
504
+            APP.UI.notifyTokenAuthFailed();
505
+        } else {
506
+            APP.UI.notifyConnectionFailed(err);
507
+        }
508
+        throw err;
509
+    });
510
+}
511
+
508 512
 function init() {
509 513
     APP.UI.start();
514
+
510 515
     JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
516
+
511 517
     JitsiMeetJS.init().then(function () {
512 518
         return Promise.all([createLocalTracks(), connect()]);
513 519
     }).then(function ([tracks, connection]) {

+ 119
- 42
lib-jitsi-meet.js 查看文件

@@ -1,6 +1,6 @@
1 1
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JitsiMeetJS = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2 2
 (function (__filename){
3
-/* global Strophe, $ */
3
+/* global Strophe, $, Promise */
4 4
 /* jshint -W101 */
5 5
 var logger = require("jitsi-meet-logger").getLogger(__filename);
6 6
 var RTC = require("./modules/RTC/RTC");
@@ -54,6 +54,13 @@ JitsiConference.prototype.join = function (password) {
54 54
         this.room.join(password, this.connection.tokenPassword);
55 55
 };
56 56
 
57
+/**
58
+ * Check if joined to the conference.
59
+ */
60
+JitsiConference.prototype.isJoined = function () {
61
+    return this.room && this.room.joined;
62
+};
63
+
57 64
 /**
58 65
  * Leaves the conference.
59 66
  */
@@ -63,6 +70,40 @@ JitsiConference.prototype.leave = function () {
63 70
     this.room = null;
64 71
 };
65 72
 
73
+/**
74
+ * Returns name of this conference.
75
+ */
76
+JitsiConference.prototype.getName = function () {
77
+    return this.options.name;
78
+};
79
+
80
+/**
81
+ * Check if external authentication is enabled for this conference.
82
+ */
83
+JitsiConference.prototype.isExternalAuthEnabled = function () {
84
+    return this.room && this.room.moderator.isExternalAuthEnabled();
85
+};
86
+
87
+/**
88
+ * Get url for external authentication.
89
+ * @param {boolean} [urlForPopup] if true then return url for login popup,
90
+ *                                else url of login page.
91
+ * @returns {Promise}
92
+ */
93
+JitsiConference.prototype.getExternalAuthUrl = function (urlForPopup) {
94
+    return new Promise(function (resolve, reject) {
95
+        if (!this.isExternalAuthEnabled()) {
96
+            reject();
97
+            return;
98
+        }
99
+        if (urlForPopup) {
100
+            this.room.moderator.getPopupLoginUrl(resolve, reject);
101
+        } else {
102
+            this.room.moderator.getLoginUrl(resolve, reject);
103
+        }
104
+    }.bind(this));
105
+};
106
+
66 107
 /**
67 108
  * Returns the local tracks.
68 109
  */
@@ -225,6 +266,11 @@ JitsiConference.prototype._fireMuteChangeEvent = function (track) {
225 266
  * @param track the JitsiLocalTrack object.
226 267
  */
227 268
 JitsiConference.prototype.removeTrack = function (track) {
269
+    if(!this.room){
270
+        if(this.rtc)
271
+            this.rtc.removeLocalStream(track);
272
+        return;
273
+    }
228 274
     this.room.removeStream(track.getOriginalStream(), function(){
229 275
         this.rtc.removeLocalStream(track);
230 276
         this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
@@ -264,7 +310,7 @@ JitsiConference.prototype.lock = function (password) {
264 310
     }, function (err) {
265 311
       reject(err);
266 312
     }, function () {
267
-      reject(JitsiConferenceErrors.PASSWORD_REQUIRED);
313
+      reject(JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED);
268 314
     });
269 315
   });
270 316
 };
@@ -475,6 +521,18 @@ function setupListeners(conference) {
475 521
     conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
476 522
         conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED);
477 523
     });
524
+    conference.room.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
525
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
526
+    });
527
+    conference.room.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
528
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
529
+    });
530
+    conference.room.addListener(XMPPEvents.PASSWORD_REQUIRED, function (pres) {
531
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.PASSWORD_REQUIRED, pres);
532
+    });
533
+    conference.room.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, function () {
534
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.AUTHENTICATION_REQUIRED);
535
+    });
478 536
 //    FIXME
479 537
 //    conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
480 538
 //        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
@@ -498,7 +556,7 @@ function setupListeners(conference) {
498 556
         conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);
499 557
     });
500 558
     conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () {
501
-        conference.eventEmitter.emit(JitsiConferenceEvents.SETUP_FAILED);
559
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.SETUP_FAILED);
502 560
     });
503 561
 
504 562
     conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) {
@@ -561,6 +619,10 @@ var JitsiConferenceErrors = {
561 619
      * Indicates that a password is required in order to join the conference.
562 620
      */
563 621
     PASSWORD_REQUIRED: "conference.passwordRequired",
622
+    /**
623
+     * Indicates that client must be authenticated to create the conference.
624
+     */
625
+    AUTHENTICATION_REQUIRED: "conference.authenticationRequired",
564 626
     /**
565 627
      * Indicates that password cannot be set for this conference.
566 628
      */
@@ -570,6 +632,10 @@ var JitsiConferenceErrors = {
570 632
      * conference.
571 633
      */
572 634
     CONNECTION_ERROR: "conference.connectionError",
635
+    /**
636
+     * Indicates that the conference setup failed.
637
+     */
638
+    SETUP_FAILED: "conference.setup_failed",
573 639
     /**
574 640
      * Indicates that there is no available videobridge.
575 641
      */
@@ -653,9 +719,9 @@ var JitsiConferenceEvents = {
653 719
      */
654 720
     CONNECTION_RESTORED: "conference.connectionRestored",
655 721
     /**
656
-     * Indicates that the conference setup failed.
722
+     * Indicates that conference failed.
657 723
      */
658
-    SETUP_FAILED: "conference.setup_failed",
724
+    CONFERENCE_FAILED: "conference.failed",
659 725
     /**
660 726
      * Indicates that conference has been joined.
661 727
      */
@@ -1346,7 +1412,6 @@ function JitsiLocalTrack(stream, videoType,
1346 1412
     this.dontFireRemoveEvent = false;
1347 1413
     this.resolution = resolution;
1348 1414
     this.startMuted = false;
1349
-    this.isLocal = true;
1350 1415
     var self = this;
1351 1416
     JitsiTrack.call(this, null, stream,
1352 1417
         function () {
@@ -1517,7 +1582,6 @@ function JitsiRemoteTrack(RTC, data, sid, ssrc) {
1517 1582
     this.videoType = data.videoType;
1518 1583
     this.ssrc = ssrc;
1519 1584
     this.muted = false;
1520
-    this.isLocal = false;
1521 1585
     if((this.type === JitsiTrack.AUDIO && data.audiomuted)
1522 1586
       || (this.type === JitsiTrack.VIDEO && data.videomuted)) {
1523 1587
         this.muted = true;
@@ -3391,9 +3455,8 @@ module.exports = ScreenObtainer;
3391 3455
 }).call(this,"/modules/RTC/ScreenObtainer.js")
3392 3456
 },{"../../service/desktopsharing/DesktopSharingEventTypes":82,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":47}],20:[function(require,module,exports){
3393 3457
 (function (__filename){
3394
-/*! adapterjs - v0.12.0 - 2015-09-04 */
3458
+/*! adapterjs - v0.12.3 - 2015-11-16 */
3395 3459
 var console = require("jitsi-meet-logger").getLogger(__filename);
3396
-
3397 3460
 // Adapter's interface.
3398 3461
 var AdapterJS = AdapterJS || {};
3399 3462
 
@@ -3411,7 +3474,7 @@ AdapterJS.options = AdapterJS.options || {};
3411 3474
 // AdapterJS.options.hidePluginInstallPrompt = true;
3412 3475
 
3413 3476
 // AdapterJS version
3414
-AdapterJS.VERSION = '0.12.0';
3477
+AdapterJS.VERSION = '0.12.3';
3415 3478
 
3416 3479
 // This function will be called when the WebRTC API is ready to be used
3417 3480
 // Whether it is the native implementation (Chrome, Firefox, Opera) or
@@ -4001,7 +4064,7 @@ if (navigator.mozGetUserMedia) {
4001 4064
 
4002 4065
   createIceServers = function (urls, username, password) {
4003 4066
     var iceServers = [];
4004
-    for (i = 0; i < urls.length; i++) {
4067
+    for (var i = 0; i < urls.length; i++) {
4005 4068
       var iceServer = createIceServer(urls[i], username, password);
4006 4069
       if (iceServer !== null) {
4007 4070
         iceServers.push(iceServer);
@@ -4091,7 +4154,7 @@ if (navigator.mozGetUserMedia) {
4091 4154
         'username' : username
4092 4155
       };
4093 4156
     } else {
4094
-      for (i = 0; i < urls.length; i++) {
4157
+      for (var i = 0; i < urls.length; i++) {
4095 4158
         var iceServer = createIceServer(urls[i], username, password);
4096 4159
         if (iceServer !== null) {
4097 4160
           iceServers.push(iceServer);
@@ -4393,12 +4456,13 @@ if (navigator.mozGetUserMedia) {
4393 4456
         return;
4394 4457
       }
4395 4458
 
4396
-      var streamId
4459
+      var streamId;
4397 4460
       if (stream === null) {
4398 4461
         streamId = '';
4399
-      }
4400
-      else {
4401
-        stream.enableSoundTracks(true); // TODO: remove on 0.12.0
4462
+      } else {
4463
+        if (typeof stream.enableSoundTracks !== 'undefined') {
4464
+          stream.enableSoundTracks(true);
4465
+        }
4402 4466
         streamId = stream.id;
4403 4467
       }
4404 4468
 
@@ -4440,16 +4504,13 @@ if (navigator.mozGetUserMedia) {
4440 4504
 
4441 4505
         var height = '';
4442 4506
         var width = '';
4443
-        if (element.getBoundingClientRect) {
4444
-          var rectObject = element.getBoundingClientRect();
4445
-          width = rectObject.width + 'px';
4446
-          height = rectObject.height + 'px';
4507
+        if (element.clientWidth || element.clientHeight) {
4508
+          width = element.clientWidth;
4509
+          height = element.clientHeight;
4447 4510
         }
4448
-        else if (element.width) {
4511
+        else if (element.width || element.height) {
4449 4512
           width = element.width;
4450 4513
           height = element.height;
4451
-        } else {
4452
-          // TODO: What scenario could bring us here?
4453 4514
         }
4454 4515
 
4455 4516
         element.parentNode.insertBefore(frag, element);
@@ -4468,19 +4529,7 @@ if (navigator.mozGetUserMedia) {
4468 4529
         element.setStreamId(streamId);
4469 4530
       }
4470 4531
       var newElement = document.getElementById(elementId);
4471
-      newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
4472
-      newElement.onplay    = (element.onplay)    ? element.onplay    : function (arg) {};
4473
-      newElement.onclick   = (element.onclick)   ? element.onclick   : function (arg) {};
4474
-      if (isIE) { // on IE the event needs to be plugged manually
4475
-        newElement.attachEvent('onplaying', newElement.onplaying);
4476
-        newElement.attachEvent('onplay', newElement.onplay);
4477
-        newElement._TemOnClick = function (id) {
4478
-          var arg = {
4479
-            srcElement : document.getElementById(id)
4480
-          };
4481
-          newElement.onclick(arg);
4482
-        };
4483
-      }
4532
+      AdapterJS.forwardEventHandlers(newElement, element, Object.getPrototypeOf(element));
4484 4533
 
4485 4534
       return newElement;
4486 4535
     };
@@ -4503,6 +4552,32 @@ if (navigator.mozGetUserMedia) {
4503 4552
       }
4504 4553
     };
4505 4554
 
4555
+    AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
4556
+
4557
+      properties = Object.getOwnPropertyNames( prototype );
4558
+
4559
+      for(prop in properties) {
4560
+        propName = properties[prop];
4561
+
4562
+        if (typeof(propName.slice) === 'function') {
4563
+          if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
4564
+            if (isIE) {
4565
+              destElem.attachEvent(propName,srcElem[propName]);
4566
+            } else {
4567
+              destElem.addEventListener(propName.slice(2), srcElem[propName], false)
4568
+            }
4569
+          } else {
4570
+            //TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
4571
+          }
4572
+        }
4573
+      }
4574
+
4575
+      var subPrototype = Object.getPrototypeOf(prototype)
4576
+      if(subPrototype != null) {
4577
+        AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
4578
+      }
4579
+    }
4580
+
4506 4581
     RTCIceCandidate = function (candidate) {
4507 4582
       if (!candidate.sdpMid) {
4508 4583
         candidate.sdpMid = '';
@@ -9659,7 +9734,7 @@ function TraceablePeerConnection(ice_config, constraints, session) {
9659 9734
     var Interop = require('sdp-interop').Interop;
9660 9735
     this.interop = new Interop();
9661 9736
     var Simulcast = require('sdp-simulcast');
9662
-    this.simulcast = new Simulcast({numOfLayers: 2, explodeRemoteSimulcast: false});
9737
+    this.simulcast = new Simulcast({numOfLayers: 3, explodeRemoteSimulcast: false});
9663 9738
 
9664 9739
     // override as desired
9665 9740
     this.trace = function (what, info) {
@@ -10441,7 +10516,7 @@ Moderator.prototype.allocateConferenceFocus =  function (callback) {
10441 10516
     );
10442 10517
 };
10443 10518
 
10444
-Moderator.prototype.getLoginUrl =  function (urlCallback) {
10519
+Moderator.prototype.getLoginUrl =  function (urlCallback, failureCallback) {
10445 10520
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10446 10521
     iq.c('login-url', {
10447 10522
         xmlns: 'http://jitsi.org/protocol/focus',
@@ -10459,14 +10534,17 @@ Moderator.prototype.getLoginUrl =  function (urlCallback) {
10459 10534
             } else {
10460 10535
                 logger.error(
10461 10536
                     "Failed to get auth url from the focus", result);
10537
+                failureCallback(result);
10462 10538
             }
10463 10539
         },
10464 10540
         function (error) {
10465 10541
             logger.error("Get auth url error", error);
10542
+            failureCallback(error);
10466 10543
         }
10467 10544
     );
10468 10545
 };
10469
-Moderator.prototype.getPopupLoginUrl =  function (urlCallback) {
10546
+
10547
+Moderator.prototype.getPopupLoginUrl = function (urlCallback, failureCallback) {
10470 10548
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10471 10549
     iq.c('login-url', {
10472 10550
         xmlns: 'http://jitsi.org/protocol/focus',
@@ -10485,10 +10563,12 @@ Moderator.prototype.getPopupLoginUrl =  function (urlCallback) {
10485 10563
             } else {
10486 10564
                 logger.error(
10487 10565
                     "Failed to get POPUP auth url from the focus", result);
10566
+               failureCallback(result);
10488 10567
             }
10489 10568
         },
10490 10569
         function (error) {
10491 10570
             logger.error('Get POPUP auth url error', error);
10571
+            failureCallback(error);
10492 10572
         }
10493 10573
     );
10494 10574
 };
@@ -10523,9 +10603,6 @@ Moderator.prototype.logout =  function (callback) {
10523 10603
 
10524 10604
 module.exports = Moderator;
10525 10605
 
10526
-
10527
-
10528
-
10529 10606
 }).call(this,"/modules/xmpp/moderator.js")
10530 10607
 },{"../../service/authentication/AuthenticationEvents":81,"../../service/xmpp/XMPPEvents":85,"../settings/Settings":21,"jitsi-meet-logger":47}],35:[function(require,module,exports){
10531 10608
 (function (__filename){

+ 100
- 0
modules/AuthHandler.js 查看文件

@@ -0,0 +1,100 @@
1
+/* global JitsiMeetJS */
2
+
3
+import LoginDialog from './UI/authentication/LoginDialog';
4
+import UIEvents from '../service/UI/UIEvents';
5
+import UIUtil from './UI/util/UIUtil';
6
+import {openConnection} from './connection';
7
+
8
+const ConferenceEvents = JitsiMeetJS.events.conference;
9
+
10
+let externalAuthWindow;
11
+let authRequiredDialog;
12
+
13
+function doExternalAuth (room, lockPassword) {
14
+    if (externalAuthWindow) {
15
+        externalAuthWindow.focus();
16
+        return;
17
+    }
18
+    if (room.isJoined()) {
19
+        room.getExternalAuthUrl(true).then(function (url) {
20
+            externalAuthWindow = LoginDialog.showExternalAuthDialog(
21
+                url,
22
+                function () {
23
+                    externalAuthWindow = null;
24
+                    room.join(lockPassword);
25
+                }
26
+            );
27
+        });
28
+    } else {
29
+        // If conference has not been started yet
30
+        // then  redirect to login page
31
+        room.getExternalAuthUrl().then(UIUtil.redirect);
32
+    }
33
+}
34
+
35
+function doXmppAuth (room, lockPassword) {
36
+    let loginDialog = LoginDialog.showAuthDialog(function (id, password) {
37
+        // auth "on the fly":
38
+        // 1. open new connection with proper id and password
39
+        // 2. connect to the room
40
+        // (this will store sessionId in the localStorage)
41
+        // 3. close new connection
42
+        // 4. reallocate focus in current room
43
+        openConnection({id, password}).then(function (connection) {
44
+            // open room
45
+            let newRoom = connection.initJitsiConference(room.getName());
46
+
47
+            newRoom.on(ConferenceEvents.CONFERENCE_FAILED, function (err) {
48
+                connection.disconnect();
49
+                loginDialog.displayError(err);
50
+            });
51
+            // FIXME finish "on the fly" auth
52
+            room.room.moderator.allocateConferenceFocus(function () {
53
+                connection.disconnect();
54
+                loginDialog.close();
55
+            });
56
+
57
+        }, function (err) {
58
+            loginDialog.displayError(err);
59
+        });
60
+    }, function () { // user canceled
61
+        loginDialog.close();
62
+    });
63
+}
64
+
65
+function authenticate (room, lockPassword) {
66
+    if (room.isExternalAuthEnabled()) {
67
+        doExternalAuth(room, lockPassword);
68
+    } else {
69
+        doXmppAuth();
70
+    }
71
+}
72
+
73
+function requireAuth(roomName) {
74
+    if (authRequiredDialog) {
75
+        return;
76
+    }
77
+
78
+    authRequiredDialog = LoginDialog.showAuthRequiredDialog(
79
+        roomName, authenticate
80
+    );
81
+}
82
+
83
+function closeAuth() {
84
+    if (externalAuthWindow) {
85
+        externalAuthWindow.close();
86
+        externalAuthWindow = null;
87
+    }
88
+
89
+    if (authRequiredDialog) {
90
+        authRequiredDialog.close();
91
+        authRequiredDialog = null;
92
+    }
93
+}
94
+
95
+
96
+export default {
97
+    authenticate,
98
+    requireAuth,
99
+    closeAuth
100
+};

+ 4
- 0
modules/UI/UI.js 查看文件

@@ -736,4 +736,8 @@ UI.updateRecordingState = function (state) {
736 736
     Toolbar.updateRecordingState(state);
737 737
 };
738 738
 
739
+UI.notifyTokenAuthFailed = function () {
740
+    messageHandler.showError("dialog.error", "dialog.tokenAuthFailed");
741
+};
742
+
739 743
 module.exports = UI;

+ 39
- 56
modules/UI/authentication/LoginDialog.js 查看文件

@@ -2,40 +2,39 @@
2 2
 
3 3
 var messageHandler = require('../util/MessageHandler');
4 4
 
5
-//FIXME: use LoginDialog to add retries to XMPP.connect method used when
6
-// anonymous domain is not enabled
5
+function getPasswordInputHtml() {
6
+    let placeholder = config.hosts.authdomain
7
+        ? "user identity"
8
+        : "user@domain.net";
9
+    let passRequiredMsg = APP.translation.translateString(
10
+        "dialog.passwordRequired"
11
+    );
12
+    return `
13
+        <h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
14
+        <input name="username" type="text" placeholder=${placeholder} autofocus>
15
+        <input name="password" type="password"
16
+               data-i18n="[placeholder]dialog.userPassword"
17
+               placeholder="user password">
18
+    `;
19
+}
7 20
 
8 21
 function Dialog(successCallback, cancelCallback) {
9
-    var message = '<h2 data-i18n="dialog.passwordRequired">';
10
-    message += APP.translation.translateString("dialog.passwordRequired");
11
-    message += '</h2>' +
12
-        '<input name="username" type="text" ';
13
-    if (config.hosts.authdomain) {
14
-        message += 'placeholder="user identity" autofocus>';
15
-    } else {
16
-        message += 'placeholder="user@domain.net" autofocus>';
17
-    }
18
-    message += '<input name="password" ' +
19
-        'type="password" data-i18n="[placeholder]dialog.userPassword"' +
20
-        ' placeholder="user password">';
21
-
22
-    var okButton = APP.translation.generateTranslationHTML("dialog.Ok");
23
-
24
-    var cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
25
-
26
-    var states = {
22
+    const states = {
27 23
         login: {
28
-            html: message,
29
-            buttons: [
30
-                { title: okButton, value: true},
31
-                { title: cancelButton, value: false}
32
-            ],
24
+            html: getPasswordInputHtml(),
25
+            buttons: [{
26
+                title: APP.translation.generateTranslationHTML("dialog.Ok"),
27
+                value: true
28
+            }, {
29
+                title: APP.translation.generateTranslationHTML("dialog.Cancel"),
30
+                value: false
31
+            }],
33 32
             focus: ':input:first',
34 33
             submit: function (e, v, m, f) {
35 34
                 e.preventDefault();
36 35
                 if (v) {
37
-                    var jid = f.username;
38
-                    var password = f.password;
36
+                    let jid = f.username;
37
+                    let password = f.password;
39 38
                     if (jid && password) {
40 39
                         connDialog.goToState('connecting');
41 40
                         successCallback(jid, password);
@@ -55,22 +54,20 @@ function Dialog(successCallback, cancelCallback) {
55 54
         finished: {
56 55
             title: APP.translation.translateString('dialog.error'),
57 56
             html:   '<div id="errorMessage"></div>',
58
-            buttons: [
59
-                {
60
-                    title: APP.translation.translateString('dialog.retry'),
61
-                    value: 'retry'
62
-                },
63
-                {
64
-                    title: APP.translation.translateString('dialog.Cancel'),
65
-                    value: 'cancel'
66
-                },
67
-            ],
57
+            buttons: [{
58
+                title: APP.translation.translateString('dialog.retry'),
59
+                value: 'retry'
60
+            }, {
61
+                title: APP.translation.generateTranslationHTML("dialog.Cancel"),
62
+                value: false
63
+            }],
68 64
             defaultButton: 0,
69 65
             submit: function (e, v, m, f) {
70 66
                 e.preventDefault();
71 67
                 if (v === 'retry') {
72 68
                     connDialog.goToState('login');
73 69
                 } else {
70
+                    // User cancelled
74 71
                     cancelCallback();
75 72
                 }
76 73
             }
@@ -104,23 +101,9 @@ function Dialog(successCallback, cancelCallback) {
104 101
     };
105 102
 }
106 103
 
107
-var LoginDialog = {
104
+const LoginDialog = {
108 105
 
109
-    /**
110
-     * Displays login prompt used to establish new XMPP connection. Given
111
-     * <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
112
-     * called when we connect successfully(status === CONNECTED) or when we fail
113
-     * to do so. On connection failure program can call Dialog.close() method in
114
-     * order to cancel or do nothing to let the user retry.
115
-     * @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
116
-     *        called when we either fail to connect or succeed(check
117
-     *        Strophe.Status).
118
-     * @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
119
-     *        Jicofo in order to create session-id after the connection is
120
-     *        established.
121
-     * @returns {Dialog}
122
-     */
123
-    show: function (successCallback, cancelCallback) {
106
+    showAuthDialog: function (successCallback, cancelCallback) {
124 107
         return new Dialog(successCallback, cancelCallback);
125 108
     },
126 109
 
@@ -156,10 +139,10 @@ var LoginDialog = {
156 139
             msg,
157 140
             true,
158 141
             buttons,
159
-            function (onSubmitEvent, submitValue) {
142
+            function (e, submitValue) {
160 143
 
161 144
                 // Do not close the dialog yet
162
-                onSubmitEvent.preventDefault();
145
+                e.preventDefault();
163 146
 
164 147
                 // Open login popup
165 148
                 if (submitValue === 'authNow') {
@@ -170,4 +153,4 @@ var LoginDialog = {
170 153
     }
171 154
 };
172 155
 
173
-module.exports = LoginDialog;
156
+export default LoginDialog;

+ 5
- 1
modules/UI/util/UIUtil.js 查看文件

@@ -114,7 +114,11 @@ import PanelToggler from "../side_pannels/SidePanelToggler";
114 114
           .filter(function (item) { return item; })
115 115
           .join(',');
116 116
         $(selector).hide();
117
-    }
117
+    },
118
+
119
+     redirect (url) {
120
+         window.location.href = url;
121
+     }
118 122
 };
119 123
 
120 124
 export default UIUtil;

+ 83
- 0
modules/connection.js 查看文件

@@ -0,0 +1,83 @@
1
+/* global APP, JitsiMeetJS, config */
2
+
3
+import LoginDialog from './UI/authentication/LoginDialog';
4
+
5
+const ConnectionEvents = JitsiMeetJS.events.connection;
6
+const ConnectionErrors = JitsiMeetJS.errors.connection;
7
+
8
+export function openConnection({retry, id, password}) {
9
+    let connection = new JitsiMeetJS.JitsiConnection(null, null, {
10
+        hosts: config.hosts,
11
+        bosh: config.bosh,
12
+        clientNode: config.clientNode
13
+    });
14
+
15
+    return new Promise(function (resolve, reject) {
16
+        connection.addEventListener(
17
+            ConnectionEvents.CONNECTION_ESTABLISHED, handleConnectionEstablished
18
+        );
19
+        connection.addEventListener(
20
+            ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
21
+        );
22
+        let authDialog;
23
+
24
+        function unsubscribe() {
25
+            connection.removeEventListener(
26
+                ConnectionEvents.CONNECTION_ESTABLISHED,
27
+                handleConnectionEstablished
28
+            );
29
+            connection.removeEventListener(
30
+                ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
31
+            );
32
+            if (authDialog) {
33
+                authDialog.close();
34
+            }
35
+        }
36
+
37
+        function handleConnectionEstablished() {
38
+            unsubscribe();
39
+            resolve(connection);
40
+        }
41
+
42
+        function handleConnectionFailed(err) {
43
+            unsubscribe();
44
+            reject(err);
45
+        }
46
+
47
+        function onConnectionFailed (err) {
48
+            console.error("CONNECTION FAILED:", err);
49
+
50
+            if (!retry) {
51
+                handleConnectionFailed(err);
52
+                return;
53
+            }
54
+
55
+            // retry only if auth failed
56
+            if (err !== ConnectionErrors.PASSWORD_REQUIRED) {
57
+                handleConnectionFailed(err);
58
+                return;
59
+            }
60
+
61
+            // do not retry if token is not valid
62
+            if (config.token) {
63
+                handleConnectionFailed(err);
64
+                return;
65
+            }
66
+
67
+            // ask for password and try again
68
+
69
+            if (authDialog) {
70
+                authDialog.displayError(err);
71
+                return;
72
+            }
73
+
74
+            authDialog = LoginDialog.showAuthDialog(
75
+                function (id, password) {
76
+                    connection.connect({id, password});
77
+                }
78
+            );
79
+        }
80
+
81
+        connection.connect(id, password);
82
+    });
83
+}

Loading…
取消
儲存