Adds functionality for authentication with external system.

This commit is contained in:
paweldomas 2014-12-16 14:54:13 +01:00
parent cc38c2641b
commit f4004656a3
11 changed files with 256 additions and 59 deletions

92
app.js
View File

@ -2,11 +2,12 @@
/* application specific logic */
var connection = null;
var authenticatedUser = false;
var authenticationWindow = null;
var activecall = null;
var RTC = null;
var nickname = null;
var sharedKey = '';
var focusJid = null;
var focusMucJid = null;
var roomUrl = null;
var roomName = null;
var ssrc2jid = {};
@ -164,6 +165,8 @@ function connect(jid, password) {
}
document.getElementById('connect').disabled = true;
console.info("My Jabber ID: " + connection.jid);
if(password)
authenticatedUser = true;
maybeDoJoin();
@ -755,6 +758,10 @@ $(document).bind('joined.muc', function (event, jid, info) {
// Once we've joined the muc show the toolbar
ToolbarToggler.showToolbar();
// Show authenticate button if needed
Toolbar.showAuthenticateButton(
Moderator.isExternalAuthEnabled() && !Moderator.isModerator());
var displayName = !config.displayJids
? info.displayName : Strophe.getResourceFromJid(jid);
@ -767,10 +774,8 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
console.log('entered', jid, info);
if (info.isFocus)
{
focusJid = jid;
focusMucJid = jid;
console.info("Ignore focus: " + jid +", real JID: " + info.jid);
// We don't want this notification for the focus.
// messageHandler.notify('Focus', 'connected', 'connected');
return;
}
@ -944,6 +949,44 @@ $(document).bind('kicked.muc', function (event, jid) {
}
});
$(document).bind('role.changed.muc', function (event, jid, member, pres) {
console.info("Role changed for " + jid + ", new role: " + member.role);
VideoLayout.showModeratorIndicator();
if (member.role === 'moderator') {
var displayName = member.displayName;
if (!displayName) {
displayName = 'Somebody';
}
messageHandler.notify(
displayName,
'connected',
'Moderator rights granted to ' + displayName + '!');
}
}
);
$(document).bind('local.role.changed.muc', function (event, jid, info, pres) {
console.info("My role changed, new role: " + info.role);
var isModerator = Moderator.isModerator();
VideoLayout.showModeratorIndicator();
Toolbar.showAuthenticateButton(
Moderator.isExternalAuthEnabled() && !isModerator);
if (isModerator) {
if (authenticationWindow) {
authenticationWindow.close();
authenticationWindow = null;
}
messageHandler.notify(
'Me', 'connected', 'Moderator rights granted !');
}
}
);
$(document).bind('passwordrequired.muc', function (event, jid) {
console.log('on password required', jid);
@ -996,6 +1039,44 @@ $(document).bind('passwordrequired.main', function (event) {
);
});
$(document).bind('auth_required.moderator', function () {
// extract room name from 'room@muc.server.net'
var room = roomName.substr(0, roomName.indexOf('@'));
messageHandler.openDialog(
'Stop',
'Authentication is required to create room:<br/>' + room,
true,
{
Authenticate: 'authNow',
Close: 'close'
},
function (onSubmitEvent, submitValue) {
console.info('On submit: ' + submitValue, submitValue);
if (submitValue === 'authNow') {
authenticateClicked();
} else {
Toolbar.showAuthenticateButton(true);
}
}
);
});
function authenticateClicked() {
// Get authentication URL
Moderator.getAuthUrl(function (url) {
// Open popup with authentication URL
authenticationWindow = messageHandler.openCenteredPopup(
url, 500, 400,
function () {
// On popup closed - retry room allocation
Moderator.allocateConferenceFocus(
roomName, doJoinAfterFocus);
authenticationWindow = null;
});
});
};
/**
* Checks if video identified by given src is desktop stream.
* @param videoSrc eg.
@ -1474,6 +1555,9 @@ $(window).bind('beforeunload', function () {
});
function disposeConference(onUnload) {
Toolbar.showAuthenticateButton(false);
var handler = getConferenceHandler();
if (handler && handler.peerconnection) {
// FIXME: probably removing streams is not required and close() should

View File

@ -42,6 +42,9 @@
.icon-recEnable:before {
content: "\e614";
}
.icon-authenticate:before {
content: "\e1ae";
}
.icon-kick1:before {
content: "\e60f";
}

View File

@ -184,6 +184,12 @@
<a class="button" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" content="Start / stop camera" onclick='toggleVideo();'>
<i id="video" class="icon-camera"></i>
</a>
<span id="authentication" style="display: none">
<div class="header_button_separator"></div>
<a class="button" data-container="body" data-toggle="popover" data-placement="bottom" content="Authenticate" onclick='authenticateClicked();'>
<i id="authButton" class="icon-avatar"></i>
</a>
</span>
<span id="recording" style="display: none">
<div class="header_button_separator"></div>
<a class="button" data-container="body" data-toggle="popover" data-placement="bottom" content="Record" onclick='toggleRecording();'>

View File

@ -80,6 +80,32 @@ var messageHandler = (function(my) {
myPrompt.on('impromptu:statechanged', stateChangedFunction);
};
/**
* Opens new popup window for given <tt>url</tt> centered over current
* window.
*
* @param url the URL to be displayed in the popup window
* @param w the width of the popup window
* @param h the height of the popup window
* @param onPopupClosed optional callback function called when popup window
* has been closed.
*/
my.openCenteredPopup = function (url, w, h, onPopupClosed) {
var l = window.screenX + (window.innerWidth / 2) - (w / 2);
var t = window.screenY + (window.innerHeight / 2) - (h / 2);
var popup = window.open(
url, '_blank',
'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + '');
if (onPopupClosed) {
var pollTimer = window.setInterval(function () {
if (popup.closed !== false) {
window.clearInterval(pollTimer);
onPopupClosed();
}
}, 200);
}
};
/**
* Shows a dialog prompting the user to send an error report.
*

View File

@ -1,4 +1,4 @@
/* global $, $iq, config, connection, focusJid, forceMuted, messageHandler,
/* global $, $iq, config, connection, focusMucJid, forceMuted, messageHandler,
setAudioMuted, Strophe, toggleAudio */
/**
* Moderate connection plugin.
@ -17,7 +17,7 @@ Strophe.addConnectionPlugin('moderate', {
},
setMute: function (jid, mute) {
console.info("set mute", mute);
var iqToFocus = $iq({to: focusJid, type: 'set'})
var iqToFocus = $iq({to: focusMucJid, type: 'set'})
.c('mute', {
xmlns: 'http://jitsi.org/jitmeet/audio',
jid: jid
@ -40,7 +40,7 @@ Strophe.addConnectionPlugin('moderate', {
},
onMute: function (iq) {
var from = iq.getAttribute('from');
if (from !== focusJid) {
if (from !== focusMucJid) {
console.warn("Ignored mute from non focus peer");
return false;
}

View File

@ -9,11 +9,21 @@ var Moderator = (function (my) {
var focusUserJid;
var getNextTimeout = Util.createExpBackoffTimer(1000);
var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
// External authentication stuff
var externalAuthEnabled = false;
my.isModerator = function () {
return connection.emuc.isModerator();
};
my.isPeerModerator = function (peerJid) {
return connection.emuc.getMemberRole(peerJid) === 'moderator';
};
my.isExternalAuthEnabled = function () {
return externalAuthEnabled;
};
my.onModeratorStatusChanged = function (isModerator) {
Toolbar.showSipCallButton(isModerator);
@ -27,25 +37,12 @@ var Moderator = (function (my) {
if (isModerator && config.etherpad_base) {
Etherpad.init();
}
$(document).trigger('local.role.moderator', [isModerator]);
};
my.init = function () {
$(document).bind(
'role.changed.muc',
function (event, jid, info, pres) {
console.info(
"Role changed for " + jid + ", new role: " + info.role);
VideoLayout.showModeratorIndicator();
}
);
$(document).bind(
'local.role.changed.muc',
function (event, jid, info, pres) {
console.info("My role changed, new role: " + info.role);
VideoLayout.showModeratorIndicator();
Moderator.onModeratorStatusChanged(Moderator.isModerator());
}
);
@ -141,6 +138,19 @@ var Moderator = (function (my) {
return elem;
};
my.parseConfigOptions = function (resultIq) {
Moderator.setFocusUserJid(
$(resultIq).find('conference').attr('focusjid'));
var extAuthParam
= $(resultIq).find('>conference>property[name=\'externalAuth\']');
if (extAuthParam.length) {
externalAuthEnabled = extAuthParam.attr('value') === 'true';
}
console.info("External authentication enabled: " + externalAuthEnabled);
};
// FIXME: we need to show the fact that we're waiting for the focus
// to the user(or that focus is not available)
my.allocateConferenceFocus = function (roomName, callback) {
@ -155,8 +165,9 @@ var Moderator = (function (my) {
// Reset both timers
getNextTimeout(true);
getNextErrorTimeout(true);
Moderator.setFocusUserJid(
$(result).find('conference').attr('focusjid'));
// Setup config options
Moderator.parseConfigOptions(result);
// Exec callback
callback();
} else {
var waitMs = getNextTimeout();
@ -171,6 +182,12 @@ var Moderator = (function (my) {
}
},
function (error) {
// Not authorized to create new room
if ($(error).find('>error>not-authorized').length) {
console.warn("Unauthorized to start the conference");
$(document).trigger('auth_required.moderator');
return;
}
var waitMs = getNextErrorTimeout();
console.error("Focus error, retry after " + waitMs, error);
// Show message
@ -188,6 +205,30 @@ var Moderator = (function (my) {
);
};
my.getAuthUrl = function (urlCallback) {
var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
iq.c('auth-url', {
xmlns: 'http://jitsi.org/protocol/focus',
room: roomName
});
connection.sendIQ(
iq,
function (result) {
var url = $(result).find('auth-url').attr('url');
if (url) {
console.info("Got auth url: " + url);
urlCallback(url);
} else {
console.error(
"Failed to get auth url fro mthe focus", result);
}
},
function (error) {
console.error("Get auth url error", error);
}
);
};
return my;
}(Moderator || {}));

6
muc.js
View File

@ -503,5 +503,11 @@ Strophe.addConnectionPlugin('emuc', {
},
isModerator: function() {
return this.role === 'moderator';
},
getMemberRole: function(peerJid) {
if (this.members[peerJid]) {
return this.members[peerJid].role;
}
return null;
}
});

View File

@ -1,4 +1,4 @@
/* global $, $iq, config, connection, focusJid, messageHandler, Moderator,
/* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
Toolbar, Util */
var Recording = (function (my) {
var recordingToken = null;
@ -13,7 +13,7 @@ var Recording = (function (my) {
// with the new recording state, according to the IQ.
my.setRecording = function (state, token, callback) {
var self = this;
var elem = $iq({to: focusJid, type: 'set'});
var elem = $iq({to: focusMucJid, type: 'set'});
elem.c('conference', {
xmlns: 'http://jitsi.org/protocol/colibri'
});

View File

@ -1,4 +1,4 @@
/* global ssrc2jid */
/* global focusMucJid, ssrc2jid */
/* jshint -W117 */
/**
* Calculates packet lost percent using the number of lost packets and the
@ -324,7 +324,7 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
};
StatsCollector.prototype.logStats = function () {
if (!focusJid) {
if (!focusMucJid) {
return;
}
@ -337,7 +337,7 @@ StatsCollector.prototype.logStats = function () {
content = Base64.encode(content);
// XEP-0337-ish
var message = $msg({to: focusJid, type: 'normal'});
var message = $msg({to: focusMucJid, type: 'normal'});
message.c('log', { xmlns: 'urn:xmpp:eventlog',
id: 'PeerConnectionStats'});
message.c('message').t(content).up();

View File

@ -221,6 +221,19 @@ var Toolbar = (function (my) {
buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
* Shows or hides authentication button
* @param show <tt>true</tt> to show or <tt>false</tt> to hide
*/
my.showAuthenticateButton = function (show) {
if (show) {
$('#authentication').css({display: "inline"});
}
else {
$('#authentication').css({display: "none"});
}
};
// Shows or hides the 'recording' button.
my.showRecordingButton = function (show) {
if (!config.enableRecording) {

View File

@ -485,7 +485,8 @@ var VideoLayout = (function (my) {
if ($('#' + videoSpanId).length > 0) {
// If there's been a focus change, make sure we add focus related
// interface!!
if (Moderator.isModerator() && $('#remote_popupmenu_' + resourceJid).length <= 0) {
if (Moderator.isModerator() && !Moderator.isPeerModerator(peerJid)
&& $('#remote_popupmenu_' + resourceJid).length <= 0) {
addRemoteVideoMenu(peerJid,
document.getElementById(videoSpanId));
}
@ -905,38 +906,42 @@ var VideoLayout = (function (my) {
{
createModeratorIndicatorElement(indicatorSpan[0]);
}
} else {
Object.keys(connection.emuc.members).forEach(function (jid) {
var member = connection.emuc.members[jid];
if (member.role === 'moderator') {
var moderatorId
= 'participant_' + Strophe.getResourceFromJid(jid);
var moderatorContainer
= document.getElementById(moderatorId);
if (Strophe.getResourceFromJid(jid) === 'focus') {
// Skip server side focus
return;
}
if (!moderatorContainer) {
console.error("No moderator container for " + jid);
return;
}
var indicatorSpan
= $('#' + moderatorId + ' .focusindicator');
if (!indicatorSpan || indicatorSpan.length === 0) {
indicatorSpan = document.createElement('span');
indicatorSpan.className = 'focusindicator';
moderatorContainer.appendChild(indicatorSpan);
createModeratorIndicatorElement(indicatorSpan);
}
}
});
}
Object.keys(connection.emuc.members).forEach(function (jid) {
var member = connection.emuc.members[jid];
if (member.role === 'moderator') {
var moderatorId
= 'participant_' + Strophe.getResourceFromJid(jid);
var moderatorContainer
= document.getElementById(moderatorId);
if (Strophe.getResourceFromJid(jid) === 'focus') {
// Skip server side focus
return;
}
if (!moderatorContainer) {
console.error("No moderator container for " + jid);
return;
}
var menuSpan = $('#' + moderatorId + '>span.remotevideomenu');
if (menuSpan.length) {
removeRemoteVideoMenu(moderatorId);
}
var indicatorSpan
= $('#' + moderatorId + ' .focusindicator');
if (!indicatorSpan || indicatorSpan.length === 0) {
indicatorSpan = document.createElement('span');
indicatorSpan.className = 'focusindicator';
moderatorContainer.appendChild(indicatorSpan);
createModeratorIndicatorElement(indicatorSpan);
}
}
});
};
/**
@ -1399,6 +1404,19 @@ var VideoLayout = (function (my) {
popupmenuElement.appendChild(paddingSpan);
}
/**
* Removes remote video menu element from video element identified by
* given <tt>videoElementId</tt>.
*
* @param videoElementId the id of local or remote video element.
*/
function removeRemoteVideoMenu(videoElementId) {
var menuSpan = $('#' + videoElementId + '>span.remotevideomenu');
if (menuSpan.length) {
menuSpan.remove();
}
}
/**
* On contact list item clicked.
*/