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

This commit is contained in:
paweldomas 2015-03-13 15:01:42 +01:00
parent 7c201573fb
commit 588c2d9e4b
7 changed files with 320 additions and 45 deletions

View File

@ -132,6 +132,7 @@
"passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.",
"joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.",
"connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
"connecting": "Connecting",
"error": "Error",
"detectext": "Error when trying to detect desktopsharing extension.",
"failtoinstall": "Failed to install desktop sharing extension",
@ -159,6 +160,7 @@
"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.",
"Authenticate": "Authenticate",
"Cancel": "Cancel",
"retry": "Retry",
"logoutTitle" : "Logout",
"logoutQuestion" : "Are you sure you want to logout and stop the conference?",
"sessTerminated": "Session Terminated",
@ -216,5 +218,20 @@
"Talk to you in a sec!"
],
"and": "and"
},
"connection":
{
"ERROR": "Error",
"CONNECTING": "Connecting",
"CONNFAIL": "Connection failed",
"AUTHENTICATING": "Authenticating",
"AUTHFAIL": "Authentication failed",
"CONNECTED": "Connected",
"DISCONNECTED": "Disconnected",
"DISCONNECTING": "Disconnecting",
"ATTACHED": "Attached",
"FETCH_SESSION_ID": "Obtaining session-id...",
"GOT_SESSION_ID": "Obtaining session-id... Done",
"GET_SESSION_ID_ERROR": "Get session-id error: "
}
}

View File

@ -1,4 +1,7 @@
/* global $, APP */
/* global $, APP*/
var LoginDialog = require('./LoginDialog');
var Moderator = require('../../xmpp/moderator');
/* Initial "authentication required" dialog */
var authDialog = null;
@ -50,6 +53,37 @@ var Authentication = {
authenticationWindow = null;
}
},
xmppAuthenticate: function () {
var loginDialog = LoginDialog.show(
function (connection, state) {
if (!state) {
// User cancelled
loginDialog.close();
return;
} else if (state == APP.xmpp.Status.CONNECTED) {
loginDialog.close();
Authentication.stopInterval();
Authentication.closeAuthenticationDialog();
// Close the connection as anonymous one will be used
// to create the conference. Session-id will authorize
// the request.
connection.disconnect();
var roomName = APP.UI.generateRoomName();
Moderator.allocateConferenceFocus(roomName, function () {
// If it's not "on the fly" authentication now join
// the conference room
if (!APP.xmpp.getMUCJoined()) {
APP.UI.checkForNicknameAndJoin();
}
});
}
}, true);
},
focusAuthenticationWindow: function () {
// If auth window exists just bring it to the front
if (authenticationWindow) {
@ -60,7 +94,7 @@ var Authentication = {
closeAuthenticationDialog: function () {
// Close authentication dialog if opened
if (authDialog) {
APP.UI.messageHandler.closeDialog();
authDialog.close();
authDialog = null;
}
},
@ -70,10 +104,7 @@ var Authentication = {
// On closed
function () {
// Close authentication dialog if opened
if (authDialog) {
messageHandler.closeDialog();
authDialog = null;
}
Authentication.closeAuthenticationDialog();
callback();
authenticationWindow = null;
});

View File

@ -0,0 +1,228 @@
/* global $, APP, config*/
var XMPP = require('../../xmpp/xmpp');
var Moderator = require('../../xmpp/moderator');
//FIXME: use LoginDialog to add retries to XMPP.connect method used when
// anonymous domain is not enabled
/**
* Creates new <tt>Dialog</tt> instance.
* @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.
* @constructor
*/
function Dialog(callback, obtainSession) {
var self = this;
var stop = false;
var connection = APP.xmpp.createConnection();
var message = '<h2 data-i18n="dialog.passwordRequired">';
message += APP.translation.translateString("dialog.passwordRequired");
message += '</h2>' +
'<input name="username" type="text" ' +
'placeholder="user@domain.net" autofocus>' +
'<input name="password" ' +
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
' placeholder="user password">';
var okButton = APP.translation.generateTranslatonHTML("dialog.Ok");
var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel");
var states = {
login: {
html: message,
buttons: [
{ title: okButton, value: true},
{ title: cancelButton, value: false}
],
focus: ':input:first',
submit: function (e, v, m, f) {
e.preventDefault();
if (v) {
var jid = f.username;
var password = f.password;
if (jid && password) {
stop = false;
connection.reset();
connDialog.goToState('connecting');
connection.connect(jid, password, stateHandler);
}
} else {
// User cancelled
stop = true;
callback();
}
}
},
connecting: {
title: APP.translation.translateString('dialog.connecting'),
html: '<div id="connectionStatus"></div>',
buttons: [],
defaultButton: 0
},
finished: {
title: APP.translation.translateString('dialog.error'),
html: '<div id="errorMessage"></div>',
buttons: [
{
title: APP.translation.translateString('dialog.retry'),
value: 'retry'
},
{
title: APP.translation.translateString('dialog.Cancel'),
value: 'cancel'
},
],
defaultButton: 0,
submit: function (e, v, m, f) {
e.preventDefault();
if (v === 'retry')
connDialog.goToState('login');
else
callback();
}
}
};
var connDialog
= APP.UI.messageHandler.openDialogWithStates(states,
{ persistent: true, closeText: '' }, null);
var stateHandler = function (status, message) {
if (stop) {
return;
}
var translateKey = "connection." + XMPP.getStatusString(status);
var statusStr = APP.translation.translateString(translateKey);
// Display current state
var connectionStatus =
connDialog.getState('connecting').find('#connectionStatus');
connectionStatus.text(statusStr);
switch (status) {
case XMPP.Status.CONNECTED:
stop = true;
if (!obtainSession) {
callback(connection, status);
return;
}
// Obtaining session-id status
connectionStatus.text(
APP.translation.translateString(
'connection.FETCH_SESSION_ID'));
// Authenticate with Jicofo and obtain session-id
var roomName = APP.UI.generateRoomName();
// Jicofo will return new session-id when connected
// from authenticated domain
connection.sendIQ(
Moderator.createConferenceIq(roomName),
function (result) {
connectionStatus.text(
APP.translation.translateString(
'connection.GOT_SESSION_ID'));
stop = true;
// Parse session-id
Moderator.parseSessionId(result);
callback(connection, status);
},
function (error) {
console.error("Auth on the fly failed", error);
stop = true;
var errorMsg =
APP.translation.translateString(
'connection.GET_SESSION_ID_ERROR') +
$(error).find('>error').attr('code');
self.displayError(errorMsg);
connection.disconnect();
});
break;
case XMPP.Status.AUTHFAIL:
case XMPP.Status.CONNFAIL:
case XMPP.Status.DISCONNECTED:
stop = true;
callback(connection, status);
var errorMessage = statusStr;
if (message)
{
errorMessage += ': ' + message;
}
self.displayError(errorMessage);
break;
default:
break;
}
};
/**
* Displays error message in 'finished' state which allows either to cancel
* or retry.
* @param message the final message to be displayed.
*/
this.displayError = function (message) {
var finishedState = connDialog.getState('finished');
var errorMessageElem = finishedState.find('#errorMessage');
errorMessageElem.text(message);
connDialog.goToState('finished');
};
/**
* Closes LoginDialog.
*/
this.close = function () {
stop = true;
connDialog.close();
};
}
var LoginDialog = {
/**
* 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 (callback, obtainSession) {
return new Dialog(callback, obtainSession);
}
};
module.exports = LoginDialog;

View File

@ -264,12 +264,6 @@ var Toolbar = (function (my) {
loggedIn = true;
}
//FIXME: XMPP authentication need improvements for "live" login
if (!APP.xmpp.isExternalAuthEnabled() && !loggedIn)
{
authenticationEnabled = false;
}
Toolbar.showAuthenticateButton(authenticationEnabled);
if (authenticationEnabled) {
@ -292,6 +286,10 @@ var Toolbar = (function (my) {
my.authenticateClicked = function () {
Authentication.focusAuthenticationWindow();
if (!APP.xmpp.isExternalAuthEnabled()) {
Authentication.xmppAuthenticate();
return;
}
// Get authentication URL
if (!APP.xmpp.getMUCJoined()) {
APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {

View File

@ -94,7 +94,7 @@ var messageHandler = (function(my) {
if (persistent) {
args.closeText = '';
}
return $.prompt(msgString, args);
return new Impromptu(msgString, args);
};
/**
@ -108,16 +108,10 @@ var messageHandler = (function(my) {
* Shows a dialog with different states to the user.
*
* @param statesObject object containing all the states of the dialog
* @param loadedFunction function to be called after the prompt is fully loaded
* @param stateChangedFunction function to be called when the state of the dialog is changed
*/
my.openDialogWithStates = function(statesObject, loadedFunction, stateChangedFunction) {
my.openDialogWithStates = function (statesObject, options) {
var myPrompt = $.prompt(statesObject);
myPrompt.on('impromptu:loaded', loadedFunction);
myPrompt.on('impromptu:statechanged', stateChangedFunction);
return new Impromptu(statesObject, options);
};
/**

View File

@ -188,6 +188,14 @@ var Moderator = {
return elem;
},
parseSessionId: function (resultIq) {
var sessionId = $(resultIq).find('conference').attr('session-id');
if (sessionId) {
console.info('Received sessionId: ' + sessionId);
localStorage.setItem('sessionId', sessionId);
}
},
parseConfigOptions: function (resultIq) {
Moderator.setFocusUserJid(
@ -209,12 +217,7 @@ var Moderator = {
if (!externalAuthEnabled) {
// We expect to receive sessionId in 'internal' authentication mode
var sessionId
= $(resultIq).find('conference').attr('session-id');
if (sessionId) {
console.info('Received sessionId: ' + sessionId);
localStorage.setItem('sessionId', sessionId);
}
Moderator.parseSessionId(resultIq);
}
var authIdentity = $(resultIq).find('>conference').attr('identity');
@ -295,22 +298,12 @@ var Moderator = {
// Not authorized to create new room
if ($(error).find('>error>not-authorized').length) {
console.warn("Unauthorized to start the conference", error);
var toDomain
= Strophe.getDomainFromJid(error.getAttribute('to'));
if (toDomain === config.hosts.anonymousdomain) {
// we are connected with anonymous domain and
// only non anonymous users can create rooms
// we must authorize the user
self.xmppService.promptLogin();
} else {
// External authentication mode
eventEmitter.emit(
XMPPEvents.AUTHENTICATION_REQUIRED,
function () {
Moderator.allocateConferenceFocus(
roomName, callback);
});
}
eventEmitter.emit(
XMPPEvents.AUTHENTICATION_REQUIRED,
function () {
Moderator.allocateConferenceFocus(
roomName, callback);
});
return;
}
var waitMs = getNextErrorTimeout();

View File

@ -13,8 +13,7 @@ var connection = null;
var authenticatedUser = false;
function connect(jid, password) {
var bosh = config.bosh || '/http-bind';
connection = new Strophe.Connection(bosh);
connection = XMPP.createConnection();
Moderator.setConnection(connection);
if (connection.disco) {
@ -127,6 +126,12 @@ function setupEvents() {
var XMPP = {
sessionTerminated: false,
/**
* XMPP connection status
*/
Status: Strophe.Status,
/**
* Remembers if we were muted by the focus.
* @type {boolean}
@ -146,7 +151,16 @@ var XMPP = {
var jid = configDomain || window.location.hostname;
connect(jid, null);
},
createConnection: function () {
var bosh = config.bosh || '/http-bind';
return new Strophe.Connection(bosh);
},
getStatusString: function (status) {
return Strophe.getStatusString(status);
},
promptLogin: function () {
// FIXME: re-use LoginDialog which supports retries
APP.UI.showLoginPopup(connect);
},
joinRoom: function(roomName, useNicks, nick)