Some big changes arround password and authentication handling

This commit is contained in:
Daniel Berteaud 2015-06-25 19:20:22 +02:00
parent b962a9e384
commit 305b6cf5eb
5 changed files with 195 additions and 155 deletions

View File

@ -127,6 +127,7 @@ our %Lexicon = (
"WRONG_PASSWORD" => "Wrong password",
"PASSWORD_REQUIRED" => "Password required",
"A_PASSWORD_IS_NEEDED_TO_JOIN" => "You must provide a password to join this room",
"ROOM_LOCKED_ENTER_OWNER_PASSWORD" => "This room is locked. Only an administrator can joint it. Please enter the administrator's password",
"TRY_AGAIN" => "Try again",
"AUTH_IF_OWNER" => "Authenticate if you are the room owner",
"CREATE_THIS_ROOM" => "Create this room",

View File

@ -133,6 +133,7 @@ our %Lexicon = (
"WRONG_PASSWORD" => "Mauvais mot de passe",
"PASSWORD_REQUIRED" => "Mot de passe requis",
"A_PASSWORD_IS_NEEDED_TO_JOIN" => "Vous devez saisir un mot de passe pour rejoindre ce salon",
"ROOM_LOCKED_ENTER_OWNER_PASSWORD" => "Ce salon est verrouillé, seul un administrateur peut le rejoindre. Veuillez saisir le mot de passe d'administration du salon",
"TRY_AGAIN" => "Essayer à nouveau",
"AUTH_IF_OWNER" => "Authentifiez-vous si vous êtes le propriétaire du salon",
"CREATE_THIS_ROOM" => "Créez ce salon",

View File

@ -32,6 +32,18 @@ var itemPerPage = 20;
// Will store the global webrtc object
var webrtc = undefined;
var roomInfo = {};
var peers = {
local: {
screenShared: false,
micMuted: false,
videoPaused: false,
displayName: '',
color: chooseColor(),
role: 'participant',
hasVideo: true
}
};
// Mark current page link as active
$('#lnk_' + page).addClass('active');
@ -239,6 +251,18 @@ $(document).on('click','button.btn-remove-email',function(e){
el.remove();
});
// Update the displayName of the peer
// and its screen if any
function updateDisplayName(id){
// We might receive the screen before the peer itself
// so check if the object exists before using it, or fallback with empty values
var display = (peers[id] && peers[id].hasName) ? stringEscape(peers[id].displayName) : '';
var color = (peers[id] && peers[id].color) ? peers[id].color : chooseColor();
var screenName = (peers[id] && peers[id].hasName) ? sprintf(localize('SCREEN_s'), stringEscape(peers[id].displayName)) : '';
$('#name_' + id).html(display).css('background-color', color);
$('#name_' + id + '_screen').html(screenName).css('background-color', color);
}
// Handle owner/join password confirm
$('#ownerPassConfirm').on('input', function() {
if ($('#ownerPassConfirm').val() == $('#ownerPass').val() &&
@ -353,6 +377,47 @@ $('#configureRoomForm').submit(function(e){
});
});
// Get our role and other room settings from the server
function getRoomInfo(cb){
$.ajax({
data: {
req: JSON.stringify({
action: 'get_room_conf',
param: {
room: roomName,
}
})
},
error: function(data){
showApiError(data);
},
success: function(data){
roomInfo = data;
// Reset the list of email displayed, so first remove evry input field but the last one
// We keep it so we can clone it again
$('.email-list').find('.email-entry:not(:last)').remove();
$.each(data.notif, function(index, obj){
addEmailInputField('email-list-notification', obj.email);
});
// Now, remove the first one if the list is not empty
if (Object.keys(data.notif).length > 0){
$('.email-list').find('.email-entry:first').remove();
}
else{
$('.email-list').find('.email-entry:first').find('input:first').val('');
}
adjustAddRemoveEmailButtons();
// Update config switches
$('#lockedSet').bootstrapSwitch('state', data.locked);
$('#askForNameSet').bootstrapSwitch('state', data.ask_for_name);
$('#joinPassSet').bootstrapSwitch('state', data.join_auth);
$('#ownerPassSet').bootstrapSwitch('state', data.owner_auth);
if (typeof cb === 'function'){
cb();
}
}
});
}
// Used on the index page
function initIndex(){
@ -667,7 +732,7 @@ function initJoin(room){
}
}
});
// Submit the join password form
$('#authBeforeJoinForm').submit(function(event){
event.preventDefault();
var pass = $('#authBeforeJoinPass').val();
@ -677,7 +742,41 @@ function initJoin(room){
}
});
function try_auth(pass, hideErrorMsg){
// This is the displayName input before joining the room
$('#displayNamePre').on('input', function() {
var name = $('#displayNamePre').val();
if (name.length > 0 && name.length < 50){
$('#displayNamePreButton').removeClass('disabled');
$('#displayNamePre').parent().removeClass('has-error');
}
else{
$('#displayNamePreButton').addClass('disabled');
$('#displayNamePre').parent().addClass('has-error');
if (name.length < 1){
$('#displayNamePre').notify(localize('DISPLAY_NAME_REQUIRED'), 'error');
}
else{
$('#displayNamePre').notify(localize('DISPLAY_NAME_TOO_LONG'), 'error');
}
}
});
$('#displayNamePreForm').submit(function(event){
event.preventDefault();
var name = $('#displayNamePre').val();
if (name.length > 0 && name.length < 50){
$('#display-name-pre').slideUp();
$('#displayName').val(name);
peers.local.hasName = true;
peers.local.displayName = name;
updateDisplayName('local');
$('#chatBox').removeAttr('disabled').removeAttr('placeholder');
init_webrtc(room);
}
});
function try_auth(pass, showErrorMsg){
$.ajax({
data: {
req: JSON.stringify({
@ -689,11 +788,19 @@ function initJoin(room){
})
},
error: function(data){
// 401 means password is missing or incorrect
// 401 means password is needed
if (data.status === 401){
data = data.responseJSON;
$('.connecting-err-reason').text(data.msg);
$('#auth-before-join').slideDown();
}
if (hideErrorMsg){
else if (data.status === 403){
data = data.responseJSON;
$('.connecting-err-reason').text(data.msg);
$('.connecting-msg').not('#room-is-locked').remove();
$('#room-is-locked').slideDown();
}
if (showErrorMsg){
showApiError(data);
}
},
@ -701,7 +808,7 @@ function initJoin(room){
$.ajax({
data: {
req: JSON.stringify({
action: 'get_rtc_conf',
action: 'get_room_conf',
param: {
room: room,
}
@ -711,22 +818,12 @@ function initJoin(room){
showApiError(data);
},
success: function(data){
if (!video){
data.config.media.video = false;
}
data.config.localVideoEl = 'webRTCVideoLocal';
webrtc = new SimpleWebRTC(data.config);
// If browser doesn't support webRTC or dataChannels
if (!webrtc.capabilities.support || !webrtc.capabilities.supportGetUserMedia || !webrtc.capabilities.supportDataChannel){
$('.connecting-msg').not('#no-webrtc-msg').remove();
$('#no-webrtc-msg').slideDown();
roomInfo = data;
if (roomInfo.ask_for_name){
$('#display-name-pre').slideDown();
}
else{
// Hide screen sharing btn if not supported, disable it on mobile
if (!webrtc.capabilities.supportScreenSharing || !$.browser.desktop){
$('.btn-share-screen').remove();
}
initVroom(room);
init_webrtc(roomName);
}
}
});
@ -736,22 +833,51 @@ function initJoin(room){
try_auth('', false);
}
function init_webrtc(room){
$.ajax({
data: {
req: JSON.stringify({
action: 'get_rtc_conf',
param: {
room: room,
}
})
},
error: function(data){
showApiError(data);
},
success: function(data){
if (!video){
data.config.media.video = false;
}
data.config.localVideoEl = 'webRTCVideoLocal';
webrtc = new SimpleWebRTC(data.config);
// Handle the readyToCall event: join the room
// Or prompt for a name first
webrtc.once('readyToCall', function () {
peers.local.id = webrtc.connection.connection.socket.sessionid;
webrtc.joinRoom(roomName);
});
// If browser doesn't support webRTC or dataChannels
if (!webrtc.capabilities.support || !webrtc.capabilities.supportGetUserMedia || !webrtc.capabilities.supportDataChannel){
$('.connecting-msg').not('#no-webrtc-msg').remove();
$('#no-webrtc-msg').slideDown();
}
else{
// Hide screen sharing btn if not supported, disable it on mobile
if (!webrtc.capabilities.supportScreenSharing || !$.browser.desktop){
$('.btn-share-screen').remove();
}
initVroom(room);
}
}
});
}
// This is the main function called when you join a room
function initVroom(room) {
// This object will be used to record all
// the peers and their info. Init with our own info
var peers = {
local: {
screenShared: false,
micMuted: false,
videoPaused: false,
displayName: '',
color: chooseColor(),
role: 'participant',
hasVideo: true
}
};
var mainVid = false,
chatHistory = {},
chatIndex = 0,
@ -772,53 +898,6 @@ function initVroom(room) {
return count;
}
// Get our role and other room settings from the server
function getRoomInfo(ev){
$.ajax({
data: {
req: JSON.stringify({
action: 'get_room_conf',
param: {
room: roomName,
}
})
},
error: function(data){
showApiError(data);
},
success: function(data){
roomInfo = data;
// Reset the list of email displayed, so first remove evry input field but the last one
// We keep it so we can clone it again
$('.email-list').find('.email-entry:not(:last)').remove();
$.each(data.notif, function(index, obj){
addEmailInputField('email-list-notification', obj.email);
});
// Now, remove the first one if the list is not empty
if (Object.keys(data.notif).length > 0){
$('.email-list').find('.email-entry:first').remove();
}
else{
$('.email-list').find('.email-entry:first').find('input:first').val('');
}
adjustAddRemoveEmailButtons();
// Update config switches
$('#lockedSet').bootstrapSwitch('state', data.locked);
$('#askForNameSet').bootstrapSwitch('state', data.ask_for_name);
$('#joinPassSet').bootstrapSwitch('state', data.join_auth);
$('#ownerPassSet').bootstrapSwitch('state', data.owner_auth);
if (ev === 'join'){
if (data.ask_for_name){
$('#display-name-pre').slideDown();
}
else{
webrtc.joinRoom(room);
}
}
}
});
}
// Get the role of a peer
function getPeerRole(id){
$.ajax({
@ -1134,18 +1213,6 @@ function initVroom(room) {
chatIndex++;
}
// Update the displayName of the peer
// and its screen if any
function updateDisplayName(id){
// We might receive the screen before the peer itself
// so check if the object exists before using it, or fallback with empty values
var display = (peers[id] && peers[id].hasName) ? stringEscape(peers[id].displayName) : '';
var color = (peers[id] && peers[id].color) ? peers[id].color : chooseColor();
var screenName = (peers[id] && peers[id].hasName) ? sprintf(localize('SCREEN_s'), stringEscape(peers[id].displayName)) : '';
$('#name_' + id).html(display).css('background-color', color);
$('#name_' + id + '_screen').html(screenName).css('background-color', color);
}
// Mute a peer
function mutePeer(id,globalAction){
if (peers[id] && peers[id].role != 'owner'){
@ -1552,13 +1619,6 @@ function initVroom(room) {
getPeerRole(data.id);
});
// Handle the readyToCall event: join the room
// Or prompt for a name first
webrtc.once('readyToCall', function () {
peers.local.id = webrtc.connection.connection.socket.sessionid;
getRoomInfo('join');
});
//Notification from the server
webrtc.connection.connection.on('notification', function(data) {
if (!data.payload.class || !data.payload.class.match(/^(success)|(info)|(warning)|(error)$/)){
@ -1794,39 +1854,6 @@ function initVroom(room) {
}, 3100);
}
});
// This is the displayName input before joining the room
$('#displayNamePre').on('input', function() {
var name = $('#displayNamePre').val();
if (name.length > 0 && name.length < 50){
$('#displayNamePreButton').removeClass('disabled');
$('#displayNamePre').parent().removeClass('has-error');
}
else{
$('#displayNamePreButton').addClass('disabled');
$('#displayNamePre').parent().addClass('has-error');
if (name.length < 1){
$('#displayNamePre').notify(localize('DISPLAY_NAME_REQUIRED'), 'error');
}
else{
$('#displayNamePre').notify(localize('DISPLAY_NAME_TOO_LONG'), 'error');
}
}
});
$('#displayNamePreForm').submit(function(event){
event.preventDefault();
var name = $('#displayNamePre').val();
if (name.length > 0 && name.length < 50){
//$('#setDisplayName').modal('hide');
$('#display-name-pre').slideUp();
$('#displayName').val(name);
peers.local.hasName = true;
peers.local.displayName = name;
updateDisplayName('local');
$('#chatBox').removeAttr('disabled').removeAttr('placeholder');
webrtc.joinRoom(room);
}
});
// ScreenSharing
$('.btn-share-screen').click(function() {

View File

@ -219,6 +219,13 @@
<p class="text-center">
<%=l 'CONNECTING_PLEASE_WAIT' %>
</p>
<p class="text-center connecting-err-reason">
</p>
</div>
<div id="room-is-locked" class="connecting-msg" style="display: none">
%= image '/img/lock.png', alt => $self->l('LOCKED'), class => "img-responsive center-block"
<p class="text-center connecting-err-reason">
</p>
</div>
<div id="no-webcam-msg" class="connecting-msg" style="display: none">
<p class="text-center">
@ -238,9 +245,6 @@
</div>
<div id="auth-before-join" class="connecting-msg" style="display: none">
<form role="form" class="form-horizontal" id="authBeforeJoinForm">
<p class="text-center">
<%=l 'A_PASSWORD_IS_NEEDED_TO_JOIN' %>
</p>
<div class="form-group">
<label for="authBeforJoinPass" class="col-sm-4 control-label">
<%=l 'PASSWORD' %>

View File

@ -839,7 +839,9 @@ helper get_key_role => sub {
$sth->execute($key->{id},$room);
$sth->bind_columns(\$key->{role});
$sth->fetch;
$self->app->log->debug("Key $token has role:" . $key->{role} . " in room $room");
if ($key->{role}){
$self->app->log->debug("Key $token has role:" . $key->{role} . " in room $room");
}
return $key->{role};
};
@ -975,7 +977,6 @@ helper get_room_conf => sub {
# Socket.IO handshake
get '/socket.io/:ver' => sub {
my $self = shift;
$self->session(peer_id => $self->get_random(256));
my $handshake = Protocol::SocketIO::Handshake->new(
session_id => $self->session('peer_id'),
heartbeat_timeout => 20,
@ -1023,7 +1024,7 @@ websocket '/socket.io/:ver/websocket/:id' => sub {
!$self->session($room)->{role} ||
$self->session($room)->{role} !~ m/^owner|participant$/){
$self->app->log->debug("Failed to connect to the signaling channel, " . $self->session('name') .
" (session ID " . $self->session('id') . ") has no role for this room");
" (session ID " . $self->session('id') . ") has no role in room $room");
$self->send( Protocol::SocketIO::Message->new( type => 'disconnect' ) );
$self->finish;
return;
@ -1464,15 +1465,19 @@ any '/api' => sub {
# Ok, now, we don't have to bother with authorization anymore
if ($req->{action} eq 'authenticate'){
my $pass = $req->{param}->{password};
# Is this peer already authenticated ?
my $role = $self->get_key_role($token, $room->{name});
my $reason;
my $code = 401;
if (!$self->session('peer_id') || $self->session('peer_id') eq ''){
$self->session(peer_id => $self->get_random(256));
}
if ($room->{owner_password} && Crypt::SaltedHash->validate($room->{owner_password}, $pass)){
$role = 'owner';
}
elsif (!$role && $room->{join_password} && Crypt::SaltedHash->validate($room->{join_password}, $pass)){
$role = 'participant';
}
elsif (!$role && !$room->{join_password}){
elsif (!$role && !$room->{join_password} && !$room->{locked}){
$role = 'participant';
}
if ($role){
@ -1492,16 +1497,30 @@ any '/api' => sub {
);
return $self->render(
json => {
msg => $self->l('AUTH_SUCCESS'),
role => $role
msg => $self->l('AUTH_SUCCESS'),
role => $role,
peer_id => $self->session('peer_id')
}
);
}
elsif ($room->{locked} && $room->{owner_password}){
$reason = $self->l('ROOM_LOCKED_ENTER_OWNER_PASSWORD');
}
elsif ($room->{locked}){
$reason = sprintf($self->l('ERROR_ROOM_s_LOCKED'), $room->{name});
$code = 403;
}
elsif ((!$pass || $pass eq '') && $room->{join_password}){
$reason = $self->l('A_PASSWORD_IS_NEEDED_TO_JOIN')
}
elsif ($room->{join_password}){
$reason = $self->l('WRONG_PASSWORD');
}
return $self->render(
json => {
msg => $self->l('WRONG_PASSWORD')
msg => $reason
},
status => '401'
status => $code
);
}
elsif ($req->{action} eq 'invite_email'){
@ -2022,18 +2041,6 @@ get '/:room' => sub {
}
# Create a session if not already done
$self->login;
# If the room is locked and we're not the owner, we cannot join it !
if ($data->{'locked'} &&
(!$self->session($room) ||
!$self->session($room)->{role} ||
$self->session($room)->{role} ne 'owner')){
return $self->render('error',
msg => sprintf($self->l("ERROR_ROOM_s_LOCKED"), $room),
err => 'ERROR_ROOM_s_LOCKED',
room => $room,
ownerPass => ($data->{owner_password}) ? '1':'0'
);
}
# If we've reached the members' limit
my $limit = $self->get_member_limit($room);
if ($limit > 0 && scalar $self->get_room_members($room) >= $limit){