Some big changes arround password and authentication handling

master
Daniel Berteaud 10 years ago
parent b962a9e384
commit 305b6cf5eb
  1. 1
      lib/Vroom/I18N/en.pm
  2. 1
      lib/Vroom/I18N/fr.pm
  3. 267
      public/js/vroom.js
  4. 10
      templates/default/join.html.ep
  5. 45
      vroom.pl

@ -127,6 +127,7 @@ our %Lexicon = (
"WRONG_PASSWORD" => "Wrong password", "WRONG_PASSWORD" => "Wrong password",
"PASSWORD_REQUIRED" => "Password required", "PASSWORD_REQUIRED" => "Password required",
"A_PASSWORD_IS_NEEDED_TO_JOIN" => "You must provide a password to join this room", "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", "TRY_AGAIN" => "Try again",
"AUTH_IF_OWNER" => "Authenticate if you are the room owner", "AUTH_IF_OWNER" => "Authenticate if you are the room owner",
"CREATE_THIS_ROOM" => "Create this room", "CREATE_THIS_ROOM" => "Create this room",

@ -133,6 +133,7 @@ our %Lexicon = (
"WRONG_PASSWORD" => "Mauvais mot de passe", "WRONG_PASSWORD" => "Mauvais mot de passe",
"PASSWORD_REQUIRED" => "Mot de passe requis", "PASSWORD_REQUIRED" => "Mot de passe requis",
"A_PASSWORD_IS_NEEDED_TO_JOIN" => "Vous devez saisir un mot de passe pour rejoindre ce salon", "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", "TRY_AGAIN" => "Essayer à nouveau",
"AUTH_IF_OWNER" => "Authentifiez-vous si vous êtes le propriétaire du salon", "AUTH_IF_OWNER" => "Authentifiez-vous si vous êtes le propriétaire du salon",
"CREATE_THIS_ROOM" => "Créez ce salon", "CREATE_THIS_ROOM" => "Créez ce salon",

@ -32,6 +32,18 @@ var itemPerPage = 20;
// Will store the global webrtc object // Will store the global webrtc object
var webrtc = undefined; var webrtc = undefined;
var roomInfo = {}; var roomInfo = {};
var peers = {
local: {
screenShared: false,
micMuted: false,
videoPaused: false,
displayName: '',
color: chooseColor(),
role: 'participant',
hasVideo: true
}
};
// Mark current page link as active // Mark current page link as active
$('#lnk_' + page).addClass('active'); $('#lnk_' + page).addClass('active');
@ -239,6 +251,18 @@ $(document).on('click','button.btn-remove-email',function(e){
el.remove(); 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 // Handle owner/join password confirm
$('#ownerPassConfirm').on('input', function() { $('#ownerPassConfirm').on('input', function() {
if ($('#ownerPassConfirm').val() == $('#ownerPass').val() && 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 // Used on the index page
function initIndex(){ function initIndex(){
@ -667,7 +732,7 @@ function initJoin(room){
} }
} }
}); });
// Submit the join password form
$('#authBeforeJoinForm').submit(function(event){ $('#authBeforeJoinForm').submit(function(event){
event.preventDefault(); event.preventDefault();
var pass = $('#authBeforeJoinPass').val(); 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({ $.ajax({
data: { data: {
req: JSON.stringify({ req: JSON.stringify({
@ -689,11 +788,19 @@ function initJoin(room){
}) })
}, },
error: function(data){ error: function(data){
// 401 means password is missing or incorrect // 401 means password is needed
if (data.status === 401){ if (data.status === 401){
data = data.responseJSON;
$('.connecting-err-reason').text(data.msg);
$('#auth-before-join').slideDown(); $('#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); showApiError(data);
} }
}, },
@ -701,6 +808,35 @@ function initJoin(room){
$.ajax({ $.ajax({
data: { data: {
req: JSON.stringify({ req: JSON.stringify({
action: 'get_room_conf',
param: {
room: room,
}
})
},
error: function(data){
showApiError(data);
},
success: function(data){
roomInfo = data;
if (roomInfo.ask_for_name){
$('#display-name-pre').slideDown();
}
else{
init_webrtc(roomName);
}
}
});
}
});
}
try_auth('', false);
}
function init_webrtc(room){
$.ajax({
data: {
req: JSON.stringify({
action: 'get_rtc_conf', action: 'get_rtc_conf',
param: { param: {
room: room, room: room,
@ -716,6 +852,13 @@ function initJoin(room){
} }
data.config.localVideoEl = 'webRTCVideoLocal'; data.config.localVideoEl = 'webRTCVideoLocal';
webrtc = new SimpleWebRTC(data.config); 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 browser doesn't support webRTC or dataChannels
if (!webrtc.capabilities.support || !webrtc.capabilities.supportGetUserMedia || !webrtc.capabilities.supportDataChannel){ if (!webrtc.capabilities.support || !webrtc.capabilities.supportGetUserMedia || !webrtc.capabilities.supportDataChannel){
$('.connecting-msg').not('#no-webrtc-msg').remove(); $('.connecting-msg').not('#no-webrtc-msg').remove();
@ -730,28 +873,11 @@ function initJoin(room){
} }
} }
}); });
}
});
}
try_auth('', false);
} }
// This is the main function called when you join a room // This is the main function called when you join a room
function initVroom(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, var mainVid = false,
chatHistory = {}, chatHistory = {},
chatIndex = 0, chatIndex = 0,
@ -772,53 +898,6 @@ function initVroom(room) {
return count; 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 // Get the role of a peer
function getPeerRole(id){ function getPeerRole(id){
$.ajax({ $.ajax({
@ -1134,18 +1213,6 @@ function initVroom(room) {
chatIndex++; 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 // Mute a peer
function mutePeer(id,globalAction){ function mutePeer(id,globalAction){
if (peers[id] && peers[id].role != 'owner'){ if (peers[id] && peers[id].role != 'owner'){
@ -1552,13 +1619,6 @@ function initVroom(room) {
getPeerRole(data.id); 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 //Notification from the server
webrtc.connection.connection.on('notification', function(data) { webrtc.connection.connection.on('notification', function(data) {
if (!data.payload.class || !data.payload.class.match(/^(success)|(info)|(warning)|(error)$/)){ if (!data.payload.class || !data.payload.class.match(/^(success)|(info)|(warning)|(error)$/)){
@ -1794,39 +1854,6 @@ function initVroom(room) {
}, 3100); }, 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 // ScreenSharing
$('.btn-share-screen').click(function() { $('.btn-share-screen').click(function() {

@ -219,6 +219,13 @@
<p class="text-center"> <p class="text-center">
<%=l 'CONNECTING_PLEASE_WAIT' %> <%=l 'CONNECTING_PLEASE_WAIT' %>
</p> </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>
<div id="no-webcam-msg" class="connecting-msg" style="display: none"> <div id="no-webcam-msg" class="connecting-msg" style="display: none">
<p class="text-center"> <p class="text-center">
@ -238,9 +245,6 @@
</div> </div>
<div id="auth-before-join" class="connecting-msg" style="display: none"> <div id="auth-before-join" class="connecting-msg" style="display: none">
<form role="form" class="form-horizontal" id="authBeforeJoinForm"> <form role="form" class="form-horizontal" id="authBeforeJoinForm">
<p class="text-center">
<%=l 'A_PASSWORD_IS_NEEDED_TO_JOIN' %>
</p>
<div class="form-group"> <div class="form-group">
<label for="authBeforJoinPass" class="col-sm-4 control-label"> <label for="authBeforJoinPass" class="col-sm-4 control-label">
<%=l 'PASSWORD' %> <%=l 'PASSWORD' %>

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

Loading…
Cancel
Save