handle external auth; xmpp auth tbd

This commit is contained in:
isymchych 2015-12-17 17:31:11 +02:00
parent 28e5bf4bec
commit 58d1c76ab0
7 changed files with 429 additions and 172 deletions

152
app.js
View File

@ -17,6 +17,11 @@ import URLProcessor from "./modules/config/URLProcessor";
import RoomnameGenerator from './modules/util/RoomnameGenerator'; import RoomnameGenerator from './modules/util/RoomnameGenerator';
import CQEvents from './service/connectionquality/CQEvents'; import CQEvents from './service/connectionquality/CQEvents';
import UIEvents from './service/UI/UIEvents'; import UIEvents from './service/UI/UIEvents';
import LoginDialog from './modules/UI/authentication/LoginDialog';
import UIUtil from './modules/UI/util/UIUtil';
import {openConnection} from './modules/connection';
import AuthHandler from './modules/AuthHandler';
import createRoomLocker from './modules/RoomLocker'; import createRoomLocker from './modules/RoomLocker';
@ -42,7 +47,7 @@ function buildRoomName () {
location ~ ^/([a-zA-Z0-9]+)$ { location ~ ^/([a-zA-Z0-9]+)$ {
rewrite ^/(.*)$ / break; rewrite ^/(.*)$ / break;
} }
*/ */
if (path.length > 1) { if (path.length > 1) {
roomName = path.substr(1).toLowerCase(); roomName = path.substr(1).toLowerCase();
} else { } else {
@ -104,62 +109,9 @@ const APP = {
const ConnectionEvents = JitsiMeetJS.events.connection; const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection; const ConnectionErrors = JitsiMeetJS.errors.connection;
function connect() {
let connection = new JitsiMeetJS.JitsiConnection(null, null, {
hosts: config.hosts,
bosh: config.bosh,
clientNode: config.clientNode
});
return new Promise(function (resolve, reject) { const ConferenceEvents = JitsiMeetJS.events.conference;
let handlers = {}; const ConferenceErrors = JitsiMeetJS.errors.conference;
function unsubscribe () {
Object.keys(handlers).forEach(function (event) {
connection.removeEventListener(event, handlers[event]);
});
}
handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
console.log('CONNECTED');
unsubscribe();
resolve(connection);
};
function listenForFailure (event) {
handlers[event] = function (...args) {
console.error(`CONNECTION FAILED: ${event}`, ...args);
unsubscribe();
reject([event, ...args]);
};
}
listenForFailure(ConnectionEvents.CONNECTION_FAILED);
listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
listenForFailure(ConnectionErrors.CONNECTION_ERROR);
listenForFailure(ConnectionErrors.OTHER_ERRORS);
// install event listeners
Object.keys(handlers).forEach(function (event) {
connection.addEventListener(event, handlers[event]);
});
connection.connect();
}).catch(function (err) {
if (err[0] === ConnectionErrors.PASSWORD_REQUIRED) {
// FIXME ask for password and try again
return connect();
}
console.error('FAILED TO CONNECT', err);
APP.UI.notifyConnectionFailed(err[1]);
throw new Error(err[0]);
});
}
var ConferenceEvents = JitsiMeetJS.events.conference;
var ConferenceErrors = JitsiMeetJS.errors.conference;
function initConference(localTracks, connection) { function initConference(localTracks, connection) {
let room = connection.initJitsiConference(APP.conference.roomName, { let room = connection.initJitsiConference(APP.conference.roomName, {
openSctp: config.openSctp, openSctp: config.openSctp,
@ -378,10 +330,6 @@ function initConference(localTracks, connection) {
APP.UI.changeDisplayName(APP.conference.localId, nickname); APP.UI.changeDisplayName(APP.conference.localId, nickname);
}); });
room.on(ConferenceErrors.CONNECTION_ERROR, function () {
// FIXME handle
});
APP.UI.addListener( APP.UI.addListener(
UIEvents.START_MUTED_CHANGED, UIEvents.START_MUTED_CHANGED,
function (startAudioMuted, startVideoMuted) { function (startAudioMuted, startVideoMuted) {
@ -461,7 +409,7 @@ function initConference(localTracks, connection) {
}); });
APP.UI.addListener(UIEvents.AUTH_CLICKED, function () { APP.UI.addListener(UIEvents.AUTH_CLICKED, function () {
// FIXME handle AuthHandler.authenticate(room);
}); });
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) { APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) {
@ -477,22 +425,67 @@ function initConference(localTracks, connection) {
}); });
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
room.on(ConferenceEvents.CONFERENCE_JOINED, resolve); room.on(ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined);
room.on(ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed);
room.on(ConferenceErrors.PASSWORD_REQUIRED, function () { let password;
APP.UI.markRoomLocked(true); let reconnectTimeout;
roomLocker.requirePassword().then(function () {
room.join(roomLocker.password);
});
});
// FIXME handle errors here function unsubscribe() {
room.off(
ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined
);
room.off(
ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed
);
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
AuthHandler.closeAuth();
}
room.join(); function handleConferenceJoined() {
}).catch(function (err) { unsubscribe();
// FIXME notify that we cannot conenct to the room resolve();
}
throw new Error(err[0]); function handleConferenceFailed(err) {
unsubscribe();
reject(err);
}
function onConferenceFailed(err, msg = '') {
console.error('CONFERENCE FAILED:', err, msg);
switch (err) {
// room is locked by the password
case ConferenceErrors.PASSWORD_REQUIRED:
APP.UI.markRoomLocked(true);
roomLocker.requirePassword().then(function () {
room.join(roomLocker.password);
});
break;
case ConferenceErrors.CONNECTION_ERROR:
APP.UI.notifyConnectionFailed(msg);
break;
// not enough rights to create conference
case ConferenceErrors.AUTHENTICATION_REQUIRED:
// schedule reconnect to check if someone else created the room
reconnectTimeout = setTimeout(function () {
room.join(password);
}, 5000);
// notify user that auth is required
AuthHandler.requireAuth(APP.conference.roomName);
break;
default:
handleConferenceFailed(err);
}
}
room.join(password);
}); });
} }
@ -505,9 +498,22 @@ function createLocalTracks () {
}); });
} }
function connect() {
return openConnection({retry: true}).catch(function (err) {
if (err === ConnectionErrors.PASSWORD_REQUIRED) {
APP.UI.notifyTokenAuthFailed();
} else {
APP.UI.notifyConnectionFailed(err);
}
throw err;
});
}
function init() { function init() {
APP.UI.start(); APP.UI.start();
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE); JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
JitsiMeetJS.init().then(function () { JitsiMeetJS.init().then(function () {
return Promise.all([createLocalTracks(), connect()]); return Promise.all([createLocalTracks(), connect()]);
}).then(function ([tracks, connection]) { }).then(function ([tracks, connection]) {

View File

@ -1,6 +1,6 @@
(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){ (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){
(function (__filename){ (function (__filename){
/* global Strophe, $ */ /* global Strophe, $, Promise */
/* jshint -W101 */ /* jshint -W101 */
var logger = require("jitsi-meet-logger").getLogger(__filename); var logger = require("jitsi-meet-logger").getLogger(__filename);
var RTC = require("./modules/RTC/RTC"); var RTC = require("./modules/RTC/RTC");
@ -54,6 +54,13 @@ JitsiConference.prototype.join = function (password) {
this.room.join(password, this.connection.tokenPassword); this.room.join(password, this.connection.tokenPassword);
}; };
/**
* Check if joined to the conference.
*/
JitsiConference.prototype.isJoined = function () {
return this.room && this.room.joined;
};
/** /**
* Leaves the conference. * Leaves the conference.
*/ */
@ -63,6 +70,40 @@ JitsiConference.prototype.leave = function () {
this.room = null; this.room = null;
}; };
/**
* Returns name of this conference.
*/
JitsiConference.prototype.getName = function () {
return this.options.name;
};
/**
* Check if external authentication is enabled for this conference.
*/
JitsiConference.prototype.isExternalAuthEnabled = function () {
return this.room && this.room.moderator.isExternalAuthEnabled();
};
/**
* Get url for external authentication.
* @param {boolean} [urlForPopup] if true then return url for login popup,
* else url of login page.
* @returns {Promise}
*/
JitsiConference.prototype.getExternalAuthUrl = function (urlForPopup) {
return new Promise(function (resolve, reject) {
if (!this.isExternalAuthEnabled()) {
reject();
return;
}
if (urlForPopup) {
this.room.moderator.getPopupLoginUrl(resolve, reject);
} else {
this.room.moderator.getLoginUrl(resolve, reject);
}
}.bind(this));
};
/** /**
* Returns the local tracks. * Returns the local tracks.
*/ */
@ -225,6 +266,11 @@ JitsiConference.prototype._fireMuteChangeEvent = function (track) {
* @param track the JitsiLocalTrack object. * @param track the JitsiLocalTrack object.
*/ */
JitsiConference.prototype.removeTrack = function (track) { JitsiConference.prototype.removeTrack = function (track) {
if(!this.room){
if(this.rtc)
this.rtc.removeLocalStream(track);
return;
}
this.room.removeStream(track.getOriginalStream(), function(){ this.room.removeStream(track.getOriginalStream(), function(){
this.rtc.removeLocalStream(track); this.rtc.removeLocalStream(track);
this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track); this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
@ -264,7 +310,7 @@ JitsiConference.prototype.lock = function (password) {
}, function (err) { }, function (err) {
reject(err); reject(err);
}, function () { }, function () {
reject(JitsiConferenceErrors.PASSWORD_REQUIRED); reject(JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED);
}); });
}); });
}; };
@ -475,6 +521,18 @@ function setupListeners(conference) {
conference.room.addListener(XMPPEvents.MUC_JOINED, function () { conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED); conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED);
}); });
conference.room.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
});
conference.room.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
});
conference.room.addListener(XMPPEvents.PASSWORD_REQUIRED, function (pres) {
conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.PASSWORD_REQUIRED, pres);
});
conference.room.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, function () {
conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.AUTHENTICATION_REQUIRED);
});
// FIXME // FIXME
// conference.room.addListener(XMPPEvents.MUC_JOINED, function () { // conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
// conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT); // conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
@ -498,7 +556,7 @@ function setupListeners(conference) {
conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED); conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);
}); });
conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () { conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () {
conference.eventEmitter.emit(JitsiConferenceEvents.SETUP_FAILED); conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.SETUP_FAILED);
}); });
conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) { conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) {
@ -561,6 +619,10 @@ var JitsiConferenceErrors = {
* Indicates that a password is required in order to join the conference. * Indicates that a password is required in order to join the conference.
*/ */
PASSWORD_REQUIRED: "conference.passwordRequired", PASSWORD_REQUIRED: "conference.passwordRequired",
/**
* Indicates that client must be authenticated to create the conference.
*/
AUTHENTICATION_REQUIRED: "conference.authenticationRequired",
/** /**
* Indicates that password cannot be set for this conference. * Indicates that password cannot be set for this conference.
*/ */
@ -570,6 +632,10 @@ var JitsiConferenceErrors = {
* conference. * conference.
*/ */
CONNECTION_ERROR: "conference.connectionError", CONNECTION_ERROR: "conference.connectionError",
/**
* Indicates that the conference setup failed.
*/
SETUP_FAILED: "conference.setup_failed",
/** /**
* Indicates that there is no available videobridge. * Indicates that there is no available videobridge.
*/ */
@ -653,9 +719,9 @@ var JitsiConferenceEvents = {
*/ */
CONNECTION_RESTORED: "conference.connectionRestored", CONNECTION_RESTORED: "conference.connectionRestored",
/** /**
* Indicates that the conference setup failed. * Indicates that conference failed.
*/ */
SETUP_FAILED: "conference.setup_failed", CONFERENCE_FAILED: "conference.failed",
/** /**
* Indicates that conference has been joined. * Indicates that conference has been joined.
*/ */
@ -1346,7 +1412,6 @@ function JitsiLocalTrack(stream, videoType,
this.dontFireRemoveEvent = false; this.dontFireRemoveEvent = false;
this.resolution = resolution; this.resolution = resolution;
this.startMuted = false; this.startMuted = false;
this.isLocal = true;
var self = this; var self = this;
JitsiTrack.call(this, null, stream, JitsiTrack.call(this, null, stream,
function () { function () {
@ -1517,7 +1582,6 @@ function JitsiRemoteTrack(RTC, data, sid, ssrc) {
this.videoType = data.videoType; this.videoType = data.videoType;
this.ssrc = ssrc; this.ssrc = ssrc;
this.muted = false; this.muted = false;
this.isLocal = false;
if((this.type === JitsiTrack.AUDIO && data.audiomuted) if((this.type === JitsiTrack.AUDIO && data.audiomuted)
|| (this.type === JitsiTrack.VIDEO && data.videomuted)) { || (this.type === JitsiTrack.VIDEO && data.videomuted)) {
this.muted = true; this.muted = true;
@ -3391,9 +3455,8 @@ module.exports = ScreenObtainer;
}).call(this,"/modules/RTC/ScreenObtainer.js") }).call(this,"/modules/RTC/ScreenObtainer.js")
},{"../../service/desktopsharing/DesktopSharingEventTypes":82,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":47}],20:[function(require,module,exports){ },{"../../service/desktopsharing/DesktopSharingEventTypes":82,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":47}],20:[function(require,module,exports){
(function (__filename){ (function (__filename){
/*! adapterjs - v0.12.0 - 2015-09-04 */ /*! adapterjs - v0.12.3 - 2015-11-16 */
var console = require("jitsi-meet-logger").getLogger(__filename); var console = require("jitsi-meet-logger").getLogger(__filename);
// Adapter's interface. // Adapter's interface.
var AdapterJS = AdapterJS || {}; var AdapterJS = AdapterJS || {};
@ -3411,7 +3474,7 @@ AdapterJS.options = AdapterJS.options || {};
// AdapterJS.options.hidePluginInstallPrompt = true; // AdapterJS.options.hidePluginInstallPrompt = true;
// AdapterJS version // AdapterJS version
AdapterJS.VERSION = '0.12.0'; AdapterJS.VERSION = '0.12.3';
// This function will be called when the WebRTC API is ready to be used // This function will be called when the WebRTC API is ready to be used
// Whether it is the native implementation (Chrome, Firefox, Opera) or // Whether it is the native implementation (Chrome, Firefox, Opera) or
@ -4001,7 +4064,7 @@ if (navigator.mozGetUserMedia) {
createIceServers = function (urls, username, password) { createIceServers = function (urls, username, password) {
var iceServers = []; var iceServers = [];
for (i = 0; i < urls.length; i++) { for (var i = 0; i < urls.length; i++) {
var iceServer = createIceServer(urls[i], username, password); var iceServer = createIceServer(urls[i], username, password);
if (iceServer !== null) { if (iceServer !== null) {
iceServers.push(iceServer); iceServers.push(iceServer);
@ -4091,7 +4154,7 @@ if (navigator.mozGetUserMedia) {
'username' : username 'username' : username
}; };
} else { } else {
for (i = 0; i < urls.length; i++) { for (var i = 0; i < urls.length; i++) {
var iceServer = createIceServer(urls[i], username, password); var iceServer = createIceServer(urls[i], username, password);
if (iceServer !== null) { if (iceServer !== null) {
iceServers.push(iceServer); iceServers.push(iceServer);
@ -4393,12 +4456,13 @@ if (navigator.mozGetUserMedia) {
return; return;
} }
var streamId var streamId;
if (stream === null) { if (stream === null) {
streamId = ''; streamId = '';
} } else {
else { if (typeof stream.enableSoundTracks !== 'undefined') {
stream.enableSoundTracks(true); // TODO: remove on 0.12.0 stream.enableSoundTracks(true);
}
streamId = stream.id; streamId = stream.id;
} }
@ -4440,16 +4504,13 @@ if (navigator.mozGetUserMedia) {
var height = ''; var height = '';
var width = ''; var width = '';
if (element.getBoundingClientRect) { if (element.clientWidth || element.clientHeight) {
var rectObject = element.getBoundingClientRect(); width = element.clientWidth;
width = rectObject.width + 'px'; height = element.clientHeight;
height = rectObject.height + 'px';
} }
else if (element.width) { else if (element.width || element.height) {
width = element.width; width = element.width;
height = element.height; height = element.height;
} else {
// TODO: What scenario could bring us here?
} }
element.parentNode.insertBefore(frag, element); element.parentNode.insertBefore(frag, element);
@ -4468,19 +4529,7 @@ if (navigator.mozGetUserMedia) {
element.setStreamId(streamId); element.setStreamId(streamId);
} }
var newElement = document.getElementById(elementId); var newElement = document.getElementById(elementId);
newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {}; AdapterJS.forwardEventHandlers(newElement, element, Object.getPrototypeOf(element));
newElement.onplay = (element.onplay) ? element.onplay : function (arg) {};
newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
if (isIE) { // on IE the event needs to be plugged manually
newElement.attachEvent('onplaying', newElement.onplaying);
newElement.attachEvent('onplay', newElement.onplay);
newElement._TemOnClick = function (id) {
var arg = {
srcElement : document.getElementById(id)
};
newElement.onclick(arg);
};
}
return newElement; return newElement;
}; };
@ -4503,6 +4552,32 @@ if (navigator.mozGetUserMedia) {
} }
}; };
AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
properties = Object.getOwnPropertyNames( prototype );
for(prop in properties) {
propName = properties[prop];
if (typeof(propName.slice) === 'function') {
if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
if (isIE) {
destElem.attachEvent(propName,srcElem[propName]);
} else {
destElem.addEventListener(propName.slice(2), srcElem[propName], false)
}
} else {
//TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
}
}
}
var subPrototype = Object.getPrototypeOf(prototype)
if(subPrototype != null) {
AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
}
}
RTCIceCandidate = function (candidate) { RTCIceCandidate = function (candidate) {
if (!candidate.sdpMid) { if (!candidate.sdpMid) {
candidate.sdpMid = ''; candidate.sdpMid = '';
@ -9659,7 +9734,7 @@ function TraceablePeerConnection(ice_config, constraints, session) {
var Interop = require('sdp-interop').Interop; var Interop = require('sdp-interop').Interop;
this.interop = new Interop(); this.interop = new Interop();
var Simulcast = require('sdp-simulcast'); var Simulcast = require('sdp-simulcast');
this.simulcast = new Simulcast({numOfLayers: 2, explodeRemoteSimulcast: false}); this.simulcast = new Simulcast({numOfLayers: 3, explodeRemoteSimulcast: false});
// override as desired // override as desired
this.trace = function (what, info) { this.trace = function (what, info) {
@ -10441,7 +10516,7 @@ Moderator.prototype.allocateConferenceFocus = function (callback) {
); );
}; };
Moderator.prototype.getLoginUrl = function (urlCallback) { Moderator.prototype.getLoginUrl = function (urlCallback, failureCallback) {
var iq = $iq({to: this.getFocusComponent(), type: 'get'}); var iq = $iq({to: this.getFocusComponent(), type: 'get'});
iq.c('login-url', { iq.c('login-url', {
xmlns: 'http://jitsi.org/protocol/focus', xmlns: 'http://jitsi.org/protocol/focus',
@ -10459,14 +10534,17 @@ Moderator.prototype.getLoginUrl = function (urlCallback) {
} else { } else {
logger.error( logger.error(
"Failed to get auth url from the focus", result); "Failed to get auth url from the focus", result);
failureCallback(result);
} }
}, },
function (error) { function (error) {
logger.error("Get auth url error", error); logger.error("Get auth url error", error);
failureCallback(error);
} }
); );
}; };
Moderator.prototype.getPopupLoginUrl = function (urlCallback) {
Moderator.prototype.getPopupLoginUrl = function (urlCallback, failureCallback) {
var iq = $iq({to: this.getFocusComponent(), type: 'get'}); var iq = $iq({to: this.getFocusComponent(), type: 'get'});
iq.c('login-url', { iq.c('login-url', {
xmlns: 'http://jitsi.org/protocol/focus', xmlns: 'http://jitsi.org/protocol/focus',
@ -10485,10 +10563,12 @@ Moderator.prototype.getPopupLoginUrl = function (urlCallback) {
} else { } else {
logger.error( logger.error(
"Failed to get POPUP auth url from the focus", result); "Failed to get POPUP auth url from the focus", result);
failureCallback(result);
} }
}, },
function (error) { function (error) {
logger.error('Get POPUP auth url error', error); logger.error('Get POPUP auth url error', error);
failureCallback(error);
} }
); );
}; };
@ -10523,9 +10603,6 @@ Moderator.prototype.logout = function (callback) {
module.exports = Moderator; module.exports = Moderator;
}).call(this,"/modules/xmpp/moderator.js") }).call(this,"/modules/xmpp/moderator.js")
},{"../../service/authentication/AuthenticationEvents":81,"../../service/xmpp/XMPPEvents":85,"../settings/Settings":21,"jitsi-meet-logger":47}],35:[function(require,module,exports){ },{"../../service/authentication/AuthenticationEvents":81,"../../service/xmpp/XMPPEvents":85,"../settings/Settings":21,"jitsi-meet-logger":47}],35:[function(require,module,exports){
(function (__filename){ (function (__filename){

100
modules/AuthHandler.js Normal file
View File

@ -0,0 +1,100 @@
/* global JitsiMeetJS */
import LoginDialog from './UI/authentication/LoginDialog';
import UIEvents from '../service/UI/UIEvents';
import UIUtil from './UI/util/UIUtil';
import {openConnection} from './connection';
const ConferenceEvents = JitsiMeetJS.events.conference;
let externalAuthWindow;
let authRequiredDialog;
function doExternalAuth (room, lockPassword) {
if (externalAuthWindow) {
externalAuthWindow.focus();
return;
}
if (room.isJoined()) {
room.getExternalAuthUrl(true).then(function (url) {
externalAuthWindow = LoginDialog.showExternalAuthDialog(
url,
function () {
externalAuthWindow = null;
room.join(lockPassword);
}
);
});
} else {
// If conference has not been started yet
// then redirect to login page
room.getExternalAuthUrl().then(UIUtil.redirect);
}
}
function doXmppAuth (room, lockPassword) {
let loginDialog = LoginDialog.showAuthDialog(function (id, password) {
// auth "on the fly":
// 1. open new connection with proper id and password
// 2. connect to the room
// (this will store sessionId in the localStorage)
// 3. close new connection
// 4. reallocate focus in current room
openConnection({id, password}).then(function (connection) {
// open room
let newRoom = connection.initJitsiConference(room.getName());
newRoom.on(ConferenceEvents.CONFERENCE_FAILED, function (err) {
connection.disconnect();
loginDialog.displayError(err);
});
// FIXME finish "on the fly" auth
room.room.moderator.allocateConferenceFocus(function () {
connection.disconnect();
loginDialog.close();
});
}, function (err) {
loginDialog.displayError(err);
});
}, function () { // user canceled
loginDialog.close();
});
}
function authenticate (room, lockPassword) {
if (room.isExternalAuthEnabled()) {
doExternalAuth(room, lockPassword);
} else {
doXmppAuth();
}
}
function requireAuth(roomName) {
if (authRequiredDialog) {
return;
}
authRequiredDialog = LoginDialog.showAuthRequiredDialog(
roomName, authenticate
);
}
function closeAuth() {
if (externalAuthWindow) {
externalAuthWindow.close();
externalAuthWindow = null;
}
if (authRequiredDialog) {
authRequiredDialog.close();
authRequiredDialog = null;
}
}
export default {
authenticate,
requireAuth,
closeAuth
};

View File

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

View File

@ -2,40 +2,39 @@
var messageHandler = require('../util/MessageHandler'); var messageHandler = require('../util/MessageHandler');
//FIXME: use LoginDialog to add retries to XMPP.connect method used when function getPasswordInputHtml() {
// anonymous domain is not enabled let placeholder = config.hosts.authdomain
? "user identity"
: "user@domain.net";
let passRequiredMsg = APP.translation.translateString(
"dialog.passwordRequired"
);
return `
<h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
<input name="username" type="text" placeholder=${placeholder} autofocus>
<input name="password" type="password"
data-i18n="[placeholder]dialog.userPassword"
placeholder="user password">
`;
}
function Dialog(successCallback, cancelCallback) { function Dialog(successCallback, cancelCallback) {
var message = '<h2 data-i18n="dialog.passwordRequired">'; const states = {
message += APP.translation.translateString("dialog.passwordRequired");
message += '</h2>' +
'<input name="username" type="text" ';
if (config.hosts.authdomain) {
message += 'placeholder="user identity" autofocus>';
} else {
message += 'placeholder="user@domain.net" autofocus>';
}
message += '<input name="password" ' +
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
' placeholder="user password">';
var okButton = APP.translation.generateTranslationHTML("dialog.Ok");
var cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
var states = {
login: { login: {
html: message, html: getPasswordInputHtml(),
buttons: [ buttons: [{
{ title: okButton, value: true}, title: APP.translation.generateTranslationHTML("dialog.Ok"),
{ title: cancelButton, value: false} value: true
], }, {
title: APP.translation.generateTranslationHTML("dialog.Cancel"),
value: false
}],
focus: ':input:first', focus: ':input:first',
submit: function (e, v, m, f) { submit: function (e, v, m, f) {
e.preventDefault(); e.preventDefault();
if (v) { if (v) {
var jid = f.username; let jid = f.username;
var password = f.password; let password = f.password;
if (jid && password) { if (jid && password) {
connDialog.goToState('connecting'); connDialog.goToState('connecting');
successCallback(jid, password); successCallback(jid, password);
@ -55,22 +54,20 @@ function Dialog(successCallback, cancelCallback) {
finished: { finished: {
title: APP.translation.translateString('dialog.error'), title: APP.translation.translateString('dialog.error'),
html: '<div id="errorMessage"></div>', html: '<div id="errorMessage"></div>',
buttons: [ buttons: [{
{ title: APP.translation.translateString('dialog.retry'),
title: APP.translation.translateString('dialog.retry'), value: 'retry'
value: 'retry' }, {
}, title: APP.translation.generateTranslationHTML("dialog.Cancel"),
{ value: false
title: APP.translation.translateString('dialog.Cancel'), }],
value: 'cancel'
},
],
defaultButton: 0, defaultButton: 0,
submit: function (e, v, m, f) { submit: function (e, v, m, f) {
e.preventDefault(); e.preventDefault();
if (v === 'retry') { if (v === 'retry') {
connDialog.goToState('login'); connDialog.goToState('login');
} else { } else {
// User cancelled
cancelCallback(); cancelCallback();
} }
} }
@ -104,23 +101,9 @@ function Dialog(successCallback, cancelCallback) {
}; };
} }
var LoginDialog = { const LoginDialog = {
/** showAuthDialog: function (successCallback, cancelCallback) {
* Displays login prompt used to establish new XMPP connection. Given
* <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
* called when we connect successfully(status === CONNECTED) or when we fail
* to do so. On connection failure program can call Dialog.close() method in
* order to cancel or do nothing to let the user retry.
* @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
* called when we either fail to connect or succeed(check
* Strophe.Status).
* @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
* Jicofo in order to create session-id after the connection is
* established.
* @returns {Dialog}
*/
show: function (successCallback, cancelCallback) {
return new Dialog(successCallback, cancelCallback); return new Dialog(successCallback, cancelCallback);
}, },
@ -156,10 +139,10 @@ var LoginDialog = {
msg, msg,
true, true,
buttons, buttons,
function (onSubmitEvent, submitValue) { function (e, submitValue) {
// Do not close the dialog yet // Do not close the dialog yet
onSubmitEvent.preventDefault(); e.preventDefault();
// Open login popup // Open login popup
if (submitValue === 'authNow') { if (submitValue === 'authNow') {
@ -170,4 +153,4 @@ var LoginDialog = {
} }
}; };
module.exports = LoginDialog; export default LoginDialog;

View File

@ -114,7 +114,11 @@ import PanelToggler from "../side_pannels/SidePanelToggler";
.filter(function (item) { return item; }) .filter(function (item) { return item; })
.join(','); .join(',');
$(selector).hide(); $(selector).hide();
} },
redirect (url) {
window.location.href = url;
}
}; };
export default UIUtil; export default UIUtil;

83
modules/connection.js Normal file
View File

@ -0,0 +1,83 @@
/* global APP, JitsiMeetJS, config */
import LoginDialog from './UI/authentication/LoginDialog';
const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection;
export function openConnection({retry, id, password}) {
let connection = new JitsiMeetJS.JitsiConnection(null, null, {
hosts: config.hosts,
bosh: config.bosh,
clientNode: config.clientNode
});
return new Promise(function (resolve, reject) {
connection.addEventListener(
ConnectionEvents.CONNECTION_ESTABLISHED, handleConnectionEstablished
);
connection.addEventListener(
ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
);
let authDialog;
function unsubscribe() {
connection.removeEventListener(
ConnectionEvents.CONNECTION_ESTABLISHED,
handleConnectionEstablished
);
connection.removeEventListener(
ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
);
if (authDialog) {
authDialog.close();
}
}
function handleConnectionEstablished() {
unsubscribe();
resolve(connection);
}
function handleConnectionFailed(err) {
unsubscribe();
reject(err);
}
function onConnectionFailed (err) {
console.error("CONNECTION FAILED:", err);
if (!retry) {
handleConnectionFailed(err);
return;
}
// retry only if auth failed
if (err !== ConnectionErrors.PASSWORD_REQUIRED) {
handleConnectionFailed(err);
return;
}
// do not retry if token is not valid
if (config.token) {
handleConnectionFailed(err);
return;
}
// ask for password and try again
if (authDialog) {
authDialog.displayError(err);
return;
}
authDialog = LoginDialog.showAuthDialog(
function (id, password) {
connection.connect({id, password});
}
);
}
connection.connect(id, password);
});
}