Allow rooms to have an optional access code (#646)

This commit is contained in:
shawn-higgins1 2019-07-22 13:12:44 -04:00 committed by Jesus Federico
parent a055b88eb7
commit 7d1c9e87a9
13 changed files with 179 additions and 34 deletions

View File

@ -44,6 +44,9 @@ $(document).on('turbolinks:load', function(){
// Display and update all fields related to creating a room in the createRoomModal // Display and update all fields related to creating a room in the createRoomModal
$("#create-room-block").click(function(){ $("#create-room-block").click(function(){
$("#create-room-name").val("") $("#create-room-name").val("")
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
$("#createRoomModal form").attr("action", $("body").data('relative-root')) $("#createRoomModal form").attr("action", $("body").data('relative-root'))
updateDropdown($(".dropdown-item[value='default']")) updateDropdown($(".dropdown-item[value='default']"))
$("#room_mute_on_join").prop("checked", false) $("#room_mute_on_join").prop("checked", false)
@ -80,6 +83,16 @@ $(document).on('turbolinks:load', function(){
}) })
updateCurrentSettings($(this).closest("#room-block").data("room-settings")) updateCurrentSettings($(this).closest("#room-block").data("room-settings"))
accessCode = $(this).closest("#room-block").data("room-access-code")
if(accessCode){
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code") %>: " + accessCode)
$("#room_access_code").val(accessCode)
} else{
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
}
}) })
//Update the createRoomModal to show the correct current settings //Update the createRoomModal to show the correct current settings
@ -107,3 +120,21 @@ function updateDropdown(element) {
$("#dropdown-trigger").text(element.text()) $("#dropdown-trigger").text(element.text())
$("#room_client").val(element.val()) $("#room_client").val(element.val())
} }
function generateAccessCode(){
const accessCodeLength = 6
var validCharacters = "0123456789"
var accessCode = ""
for( var i = 0; i < accessCodeLength; i++){
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
}
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code") %>: " + accessCode)
$("#room_access_code").val(accessCode)
}
function ResetAccessCode(){
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
}

View File

@ -65,3 +65,7 @@
background-color: rgba(0, 0, 0, 0.04); background-color: rgba(0, 0, 0, 0.04);
} }
} }
.allow-icon-click{
pointer-events: auto;
}

View File

@ -25,7 +25,7 @@ class RoomsController < ApplicationController
before_action :validate_verified_email, except: [:show, :join], before_action :validate_verified_email, except: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification } unless: -> { !Rails.configuration.enable_email_verification }
before_action :find_room, except: :create before_action :find_room, except: :create
before_action :verify_room_ownership, except: [:create, :show, :join, :logout] before_action :verify_room_ownership, except: [:create, :show, :join, :logout, :login]
before_action :verify_room_owner_verified, only: [:show, :join], before_action :verify_room_owner_verified, only: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification } unless: -> { !Rails.configuration.enable_email_verification }
before_action :verify_user_not_admin, only: [:show] before_action :verify_user_not_admin, only: [:show]
@ -36,7 +36,7 @@ class RoomsController < ApplicationController
return redirect_to current_user.main_room, flash: { alert: I18n.t("room.room_limit") } if room_limit_exceeded return redirect_to current_user.main_room, flash: { alert: I18n.t("room.room_limit") } if room_limit_exceeded
@room = Room.new(name: room_params[:name]) @room = Room.new(name: room_params[:name], access_code: room_params[:access_code])
@room.owner = current_user @room.owner = current_user
@room.room_settings = create_room_settings_string(room_params[:mute_on_join], room_params[:client]) @room.room_settings = create_room_settings_string(room_params[:mute_on_join], room_params[:client])
@ -106,6 +106,12 @@ class RoomsController < ApplicationController
opts = default_meeting_options opts = default_meeting_options
unless @room.owned_by?(current_user) unless @room.owned_by?(current_user)
# Don't allow users to join unless they have a valid access code or the room doesn't
# have an access code
if @room.access_code && !@room.access_code.empty? && @room.access_code != session[:access_code]
return redirect_to room_path(room_uid: params[:room_uid]), flash: { alert: I18n.t("room.access_code_required") }
end
# Assign join name if passed. # Assign join name if passed.
if params[@room.invite_path] if params[@room.invite_path]
@join_name = params[@room.invite_path][:join_name] @join_name = params[@room.invite_path][:join_name]
@ -118,31 +124,7 @@ class RoomsController < ApplicationController
# create or update cookie with join name # create or update cookie with join name
cookies.encrypted[:greenlight_name] = @join_name unless cookies.encrypted[:greenlight_name] == @join_name cookies.encrypted[:greenlight_name] = @join_name unless cookies.encrypted[:greenlight_name] == @join_name
if @room.running? || @room.owned_by?(current_user) join_room(opts)
# Determine if the user needs to join as a moderator.
opts[:user_is_moderator] = @room.owned_by?(current_user)
# Check if the user has specified which client to use
room_settings = JSON.parse(@room[:room_settings])
opts[:join_via_html5] = room_settings["joinViaHtml5"] if room_settings["joinViaHtml5"]
if current_user
redirect_to @room.join_path(current_user.name, opts, current_user.uid)
else
join_name = params[:join_name] || params[@room.invite_path][:join_name]
redirect_to @room.join_path(join_name, opts)
end
else
search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs =
public_recordings(@room.bbb_id, @user_domain, search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
# They need to wait until the meeting begins.
render :wait
end
end end
# DELETE /:room_uid # DELETE /:room_uid
@ -185,6 +167,8 @@ class RoomsController < ApplicationController
update_room_attributes("settings") update_room_attributes("settings")
# Update the rooms name if it has been changed # Update the rooms name if it has been changed
update_room_attributes("name") if @room.name != room_params[:name] update_room_attributes("name") if @room.name != room_params[:name]
# Update the room's access code if it has changed
update_room_attributes("access_code") if @room.access_code != room_params[:access_code]
rescue StandardError rescue StandardError
flash[:alert] = I18n.t("room.update_settings_error") flash[:alert] = I18n.t("room.update_settings_error")
else else
@ -199,6 +183,15 @@ class RoomsController < ApplicationController
redirect_to @room redirect_to @room
end end
# POST /:room_uid/login
def login
session[:access_code] = room_params[:access_code]
flash[:alert] = I18n.t("room.access_code_required") if session[:access_code] != @room.access_code
redirect_to room_path(@room.uid)
end
private private
def update_room_attributes(update_type) def update_room_attributes(update_type)
@ -208,6 +201,8 @@ class RoomsController < ApplicationController
elsif update_type.eql? "settings" elsif update_type.eql? "settings"
room_settings_string = create_room_settings_string(room_params[:mute_on_join], room_params[:client]) room_settings_string = create_room_settings_string(room_params[:mute_on_join], room_params[:client])
@room.update_attributes(room_settings: room_settings_string) @room.update_attributes(room_settings: room_settings_string)
elsif update_type.eql? "access_code"
@room.update_attributes(access_code: room_params[:access_code])
end end
end end
end end
@ -226,7 +221,7 @@ class RoomsController < ApplicationController
end end
def room_params def room_params
params.require(:room).permit(:name, :auto_join, :mute_on_join, :client) params.require(:room).permit(:name, :auto_join, :mute_on_join, :client, :access_code)
end end
# Find the room from the uid. # Find the room from the uid.
@ -292,4 +287,31 @@ class RoomsController < ApplicationController
current_user.rooms.count >= limit current_user.rooms.count >= limit
end end
def join_room(opts)
if @room.running? || @room.owned_by?(current_user)
# Determine if the user needs to join as a moderator.
opts[:user_is_moderator] = @room.owned_by?(current_user)
# Check if the user has specified which client to use
room_settings = JSON.parse(@room[:room_settings])
opts[:join_via_html5] = room_settings["joinViaHtml5"] if room_settings["joinViaHtml5"]
if current_user
redirect_to @room.join_path(current_user.name, opts, current_user.uid)
else
join_name = params[:join_name] || params[@room.invite_path][:join_name]
redirect_to @room.join_path(join_name, opts)
end
else
search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs =
public_recordings(@room.bbb_id, @user_domain, search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
# They need to wait until the meeting begins.
render :wait
end
end
end end

View File

@ -13,9 +13,21 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%> %>
<%= render 'shared/room_event' do %> <% valid_access_code = @room.access_code.nil? || @room.access_code.empty? || @room.access_code == session[:access_code] %>
<%= render 'shared/room_event', render_recordings: valid_access_code do %>
<% if room_authentication_required %> <% if room_authentication_required %>
<h2><%= t("administrator.site_settings.authentication.user-info") %></h2> <h2><%= t("administrator.site_settings.authentication.user-info") %></h2>
<% elsif !valid_access_code %>
<%= form_for :room, url: login_room_path(@room.uid) do |f| %>
<div class="input-group join-input">
<%= f.text_field :access_code,
required: true,
class: "form-control join-form",
placeholder: t("room.enter_the_access_code"),
value: "" %>
<%= f.submit t("room.login"), class: "btn btn-primary btn-sm col-sm-3 form-control join-form" %>
</div>
<% end %>
<% else %> <% else %>
<%= form_for room_path(@room), method: :post do |f| %> <%= form_for room_path(@room), method: :post do |f| %>
<div class="input-group join-input"> <div class="input-group join-input">

View File

@ -13,7 +13,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%> %>
<%= render 'shared/room_event' do %> <%= render 'shared/room_event', render_recordings: true do %>
<div class="row"> <div class="row">
<div class="col-9"> <div class="col-9">
<h3><%= t("room.wait.message") %></h3> <h3><%= t("room.wait.message") %></h3>

View File

@ -40,4 +40,6 @@
</div> </div>
</div> </div>
<%= render "shared/sessions", recordings: @public_recordings, pagy: @pagy, only_public: true, user_recordings: false, title: t("room.recordings") %> <% if render_recordings %>
<%= render "shared/sessions", recordings: @public_recordings, pagy: @pagy, only_public: true, user_recordings: false, title: t("room.recordings") %>
<% end %>

View File

@ -13,7 +13,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%> %>
<div id="<%= if room == current_user.main_room then 'home_room_block' else 'room-block' end %>" data-room-uid="<%= room.uid %>" data-room-settings=<%= room.room_settings %> class="card"> <div id="<%= if room == current_user.main_room then 'home_room_block' else 'room-block' end %>" data-room-uid="<%= room.uid %>" data-room-settings=<%= room.room_settings %> data-room-access-code="<%= room.access_code %>" class="card">
<div class="card-body p-1"> <div class="card-body p-1">
<table class="table table-hover table-vcenter text-wrap table-no-border"> <table class="table table-hover table-vcenter text-wrap table-no-border">
<tbody class="no-border-top"> <tbody class="no-border-top">

View File

@ -28,10 +28,21 @@
<span class="input-icon-addon"> <span class="input-icon-addon">
<i class="fas fa-chalkboard-teacher"></i> <i class="fas fa-chalkboard-teacher"></i>
</span> </span>
<%= f.text_field :name, id: "create-room-name", class: "form-control", value: "", placeholder: t("modal.create_room.name_placeholder"), autocomplete: :off %> <%= f.text_field :name, id: "create-room-name", class: "form-control text-center", value: "", placeholder: t("modal.create_room.name_placeholder"), autocomplete: :off %>
<div class="invalid-feedback text-left"><%= t("modal.create_room.not_blank") %></div> <div class="invalid-feedback text-left"><%= t("modal.create_room.not_blank") %></div>
</div> </div>
<div class="input-icon mb-2">
<span onclick="generateAccessCode()" class="input-icon-addon allow-icon-click">
<i class="fas fa-dice"></i>
</span>
<%= f.label :access_code, t("modal.create_room.access_code_placeholder"), id: "create-room-access-code", class: "form-control" %>
<%= f.hidden_field :access_code %>
<span onclick="ResetAccessCode()" class="input-icon-addon allow-icon-click">
<i class="far fa-trash-alt"></i>
</span>
</div>
<% if Rails.configuration.room_features.include? "default-client" %> <% if Rails.configuration.room_features.include? "default-client" %>
<label class="mt-3 mb-3 w-100 text-left d-inline-block"> <label class="mt-3 mb-3 w-100 text-left d-inline-block">
<input type="hidden" name="room[client]" id="room_client"> <input type="hidden" name="room[client]" id="room_client">

View File

@ -252,6 +252,8 @@ en:
max_concurrent: The maximum number of concurrent sessions allowed has been reached! max_concurrent: The maximum number of concurrent sessions allowed has been reached!
modal: modal:
create_room: create_room:
access_code: Access Code
access_code_placeholder: Generate an optional room access code
auto_join: Automatically join me into the room auto_join: Automatically join me into the room
create: Create Room create: Create Room
free_delete: You will be free to delete this room at any time. free_delete: You will be free to delete this room at any time.
@ -357,13 +359,16 @@ en:
super_admin: Super Admin super_admin: Super Admin
user: User user: User
room: room:
access_code_required: Please enter a valid access code to join the room
create_room: Create a Room create_room: Create a Room
create_room_error: There was an error creating the room create_room_error: There was an error creating the room
create_room_success: Room created successfully create_room_success: Room created successfully
enter_the_access_code: Enter the room's access code
invited: You have been invited to join invited: You have been invited to join
invite_participants: Invite Participants invite_participants: Invite Participants
join: Join join: Join
last_session: Last session on %{session} last_session: Last session on %{session}
login: Login
owner: Owner owner: Owner
no_sessions: This room has no sessions, yet! no_sessions: This room has no sessions, yet!
recordings: Room Recordings recordings: Room Recordings

View File

@ -102,6 +102,7 @@ Rails.application.routes.draw do
post '/update_settings', to: 'rooms#update_settings' post '/update_settings', to: 'rooms#update_settings'
post '/start', to: 'rooms#start', as: :start_room post '/start', to: 'rooms#start', as: :start_room
get '/logout', to: 'rooms#logout', as: :logout_room get '/logout', to: 'rooms#logout', as: :logout_room
post '/login', to: 'rooms#login', as: :login_room
end end
# Recording operations routes # Recording operations routes

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddPasswordDigestToRooms < ActiveRecord::Migration[5.0]
def change
add_column :rooms, :access_code, :string, null: true, default: nil
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190522195242) do ActiveRecord::Schema.define(version: 20190711192033) do
create_table "features", force: :cascade do |t| create_table "features", force: :cascade do |t|
t.integer "setting_id" t.integer "setting_id"
@ -56,6 +56,7 @@ ActiveRecord::Schema.define(version: 20190522195242) do
t.string "room_settings", default: "{ }" t.string "room_settings", default: "{ }"
t.string "moderator_pw" t.string "moderator_pw"
t.string "attendee_pw" t.string "attendee_pw"
t.string "access_code"
t.index ["bbb_id"], name: "index_rooms_on_bbb_id" t.index ["bbb_id"], name: "index_rooms_on_bbb_id"
t.index ["last_session"], name: "index_rooms_on_last_session" t.index ["last_session"], name: "index_rooms_on_last_session"
t.index ["name"], name: "index_rooms_on_name" t.index ["name"], name: "index_rooms_on_name"

View File

@ -203,6 +203,32 @@ describe RoomsController, type: :controller do
expect(response).to render_template(:wait) expect(response).to render_template(:wait)
end end
it "should render wait if the correct access code is supplied" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false)
protected_room = Room.new(name: 'test', access_code: "123456")
protected_room.owner = @owner
protected_room.save
@request.session[:user_id] = @user.id
post :join, params: { room_uid: protected_room, join_name: @user.name }, session: { access_code: "123456" }
expect(response).to render_template(:wait)
end
it "should redirect to login if the correct access code isn't supplied" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false)
protected_room = Room.new(name: 'test', access_code: "123456")
protected_room.owner = @owner
protected_room.save
@request.session[:user_id] = @user.id
post :join, params: { room_uid: protected_room, join_name: @user.name }, session: { access_code: "123455" }
expect(response).to redirect_to room_path(protected_room.uid)
end
it "should join owner as moderator if meeting running" do it "should join owner as moderator if meeting running" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true) allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true)
@ -374,4 +400,27 @@ describe RoomsController, type: :controller do
expect(response).to redirect_to(@room) expect(response).to redirect_to(@room)
end end
end end
describe "POST #login" do
before do
@user = create(:user)
@room = @user.main_room
@room.access_code = "123456"
@room.save
end
it "should redirect to show with valid access code" do
post :login, params: { room_uid: @room.uid, room: { access_code: "123456" } }
expect(response).to redirect_to room_path(@room.uid)
expect(flash[:alert]).to be_nil
end
it "should redirect to show with and notify user of invalid access code" do
post :login, params: { room_uid: @room.uid, room: { access_code: "123455" } }
expect(response).to redirect_to room_path(@room.uid)
expect(flash[:alert]).to eq(I18n.t("room.access_code_required"))
end
end
end end