Passed
Push — master ( 33ca92...23b088 )
by Ahmad
10:28
created

RoomsController   F

Complexity

Total Complexity 99

Size/Duplication

Total Lines 436
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
dl 0
loc 436
rs 2
c 3
b 1
f 0
wmc 99

34 Methods

Rating   Name   Duplication   Size   Complexity  
B show() 0 26 7
A validate_accepted_terms() 0 3 3
A verify_room_ownership_or_admin_or_shared() 0 5 4
A valid_file_type() 0 4 1
A remove_presentation() 0 12 2
A start() 0 25 2
A room_params() 0 5 1
A room_limit_exceeded() 0 9 3
A verify_user_not_admin() 0 3 2
A remove_shared_access() 0 11 2
A record_meeting() 0 8 2
A update_settings() 0 22 4
A login() 0 7 2
C join() 0 29 11
A shared_users() 0 6 1
B create() 0 24 5
A current_presentation() 0 8 1
A create_room_settings_string() 0 11 1
C room_setting_with_config() 0 23 9
A room_shared_with_user() 0 3 2
A preupload_presentation() 0 13 3
A destroy() 0 17 4
A shared_access() 0 26 3
A cant_create_rooms() 0 12 3
A find_room() 0 3 1
A verify_room_ownership_or_shared() 0 3 3
A auth_required() 0 3 1
A room_settings() 0 6 1
A verify_room_owner_valid() 0 3 3
A verify_room_owner_verified() 0 3 2
A join_specific_room() 0 8 2
A verify_room_ownership_or_admin() 0 4 3
A validate_verified_email() 0 3 3
A logout() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like RoomsController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# frozen_string_literal: true
2
3
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
4
#
5
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
6
#
7
# This program is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU Lesser General Public License as published by the Free Software
9
# Foundation; either version 3.0 of the License, or (at your option) any later
10
# version.
11
#
12
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
15
#
16
# You should have received a copy of the GNU Lesser General Public License along
17
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
18
19
class RoomsController < ApplicationController
20
  include Pagy::Backend
21
  include Recorder
22
  include Joiner
23
  include Populator
24
25
  before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
26
  before_action :validate_verified_email, except: [:show, :join],
27
                unless: -> { !Rails.configuration.enable_email_verification }
28
  before_action :find_room, except: [:create, :join_specific_room, :cant_create_rooms]
29
  before_action :verify_room_ownership_or_admin_or_shared, only: [:start, :shared_access]
30
  before_action :verify_room_ownership_or_admin, only: [:update_settings, :destroy, :preupload_presentation, :remove_presentation]
31
  before_action :verify_room_ownership_or_shared, only: [:remove_shared_access]
32
  before_action :verify_room_owner_verified, only: [:show, :join],
33
                unless: -> { !Rails.configuration.enable_email_verification }
34
  before_action :verify_room_owner_valid, only: [:show, :join]
35
  before_action :verify_user_not_admin, only: [:show]
36
  skip_before_action :verify_authenticity_token, only: [:join]
37
38
  # POST /
39
  def create
40
    # Return to root if user is not signed in
41
    return redirect_to root_path unless current_user
42
43
    # Check if the user has not exceeded the room limit
44
    return redirect_to current_user.main_room, flash: { alert: I18n.t("room.room_limit") } if room_limit_exceeded
45
46
    # Create room
47
    @room = Room.new(name: room_params[:name], access_code: room_params[:access_code])
48
    @room.owner = current_user
49
    @room.room_settings = create_room_settings_string(room_params)
50
51
    # Save the room and redirect if it fails
52
    return redirect_to current_user.main_room, flash: { alert: I18n.t("room.create_room_error") } unless @room.save
53
54
    logger.info "Support: #{current_user.email} has created a new room #{@room.uid}."
55
56
    # Redirect to room is auto join was not turned on
57
    return redirect_to @room,
58
      flash: { success: I18n.t("room.create_room_success") } unless room_params[:auto_join] == "1"
59
60
    # Start the room if auto join was turned on
61
    start
62
  end
63
64
  # GET /:room_uid
65
  def show
66
    @room_settings = JSON.parse(@room[:room_settings])
67
    @anyone_can_start = room_setting_with_config("anyoneCanStart")
68
    @room_running = room_running?(@room.bbb_id)
69
    @shared_room = room_shared_with_user
70
71
    # If its the current user's room
72
    if current_user && (@room.owned_by?(current_user) || @shared_room)
73
      # If the user is trying to access their own room but is not allowed to
74
      if @room.owned_by?(current_user) && !current_user.role.get_permission("can_create_rooms")
75
        return redirect_to cant_create_rooms_path
76
      end
77
78
      # User is allowed to have rooms
79
      @search, @order_column, @order_direction, recs =
80
        recordings(@room.bbb_id, params.permit(:search, :column, :direction), true)
81
82
      @user_list = shared_user_list if shared_access_allowed
83
84
      @pagy, @recordings = pagy_array(recs)
85
    else
86
      return redirect_to root_path, flash: { alert: I18n.t("room.invalid_provider") } if incorrect_user_domain
87
88
      show_user_join
89
    end
90
  end
91
92
  # GET /rooms
93
  def cant_create_rooms
94
    return redirect_to root_path unless current_user
95
    shared_rooms = current_user.shared_rooms
96
97
    if current_user.shared_rooms.empty?
98
      # Render view for users that cant create rooms
99
      @recent_rooms = Room.where(id: cookies.encrypted["#{current_user.uid}_recently_joined_rooms"])
100
      render :cant_create_rooms
101
    else
102
      redirect_to shared_rooms[0]
103
    end
104
  end
105
106
  # POST /:room_uid
107
  def join
108
    return redirect_to root_path,
109
      flash: { alert: I18n.t("administrator.site_settings.authentication.user-info") } if auth_required
110
111
    @shared_room = room_shared_with_user
112
113
    unless @room.owned_by?(current_user) || @shared_room
114
      # Don't allow users to join unless they have a valid access code or the room doesn't have an access code
115
      if @room.access_code && [email protected]_code.empty? && @room.access_code != session[:access_code]
116
        return redirect_to room_path(room_uid: params[:room_uid]), flash: { alert: I18n.t("room.access_code_required") }
117
      end
118
119
      # Assign join name if passed.
120
      if params[@room.invite_path]
121
        @join_name = params[@room.invite_path][:join_name]
122
      elsif !params[:join_name]
123
        # Join name not passed.
124
        return redirect_to root_path
125
      end
126
    end
127
128
    # create or update cookie with join name
129
    cookies.encrypted[:greenlight_name] = @join_name unless cookies.encrypted[:greenlight_name] == @join_name
130
131
    save_recent_rooms
132
133
    logger.info "Support: #{current_user.present? ? current_user.email : @join_name} is joining room #{@room.uid}"
134
    join_room(default_meeting_options)
135
  end
136
137
  # DELETE /:room_uid
138
  def destroy
139
    begin
140
      # Don't delete the users home room.
141
      raise I18n.t("room.delete.home_room") if @room == @room.owner.main_room
142
      @room.destroy
143
    rescue => e
144
      flash[:alert] = I18n.t("room.delete.fail", error: e)
145
    else
146
      flash[:success] = I18n.t("room.delete.success")
147
    end
148
149
    # Redirect to home room if the redirect_back location is the deleted room
150
    return redirect_to @current_user.main_room if request.referer == room_url(@room)
151
152
    # Redirect to the location that the user deleted the room from
153
    redirect_back fallback_location: current_user.main_room
154
  end
155
156
  # POST /room/join
157
  def join_specific_room
158
    room_uid = params[:join_room][:url].split('/').last
159
160
    @room = Room.find_by(uid: room_uid)
161
    return redirect_to cant_create_rooms_path, alert: I18n.t("room.no_room.invalid_room_uid") unless @room
162
163
    redirect_to room_path(@room)
164
  end
165
166
  # POST /:room_uid/start
167
  def start
168
    logger.info "Support: #{current_user.email} is starting room #{@room.uid}"
169
170
    # Join the user in and start the meeting.
171
    opts = default_meeting_options
172
    opts[:user_is_moderator] = true
173
174
    # Include the user's choices for the room settings
175
    @room_settings = JSON.parse(@room[:room_settings])
176
    opts[:mute_on_start] = room_setting_with_config("muteOnStart")
177
    opts[:require_moderator_approval] = room_setting_with_config("requireModeratorApproval")
178
    opts[:record] = record_meeting
179
180
    begin
181
      redirect_to join_path(@room, current_user.name, opts, current_user.uid)
182
    rescue BigBlueButton::BigBlueButtonException => e
183
      logger.error("Support: #{@room.uid} start failed: #{e}")
184
185
      redirect_to room_path, alert: I18n.t(e.key.to_s.underscore, default: I18n.t("bigbluebutton_exception"))
186
    end
187
188
    # Notify users that the room has started.
189
    # Delay 5 seconds to allow for server start, although the request will retry until it succeeds.
190
    NotifyUserWaitingJob.set(wait: 5.seconds).perform_later(@room)
191
  end
192
193
  # POST /:room_uid/update_settings
194
  def update_settings
195
    begin
196
      options = params[:room].nil? ? params : params[:room]
197
      raise "Room name can't be blank" if options[:name].blank?
198
199
      # Update the rooms values
200
      room_settings_string = create_room_settings_string(options)
201
202
      @room.update_attributes(
203
        name: options[:name],
204
        room_settings: room_settings_string,
205
        access_code: options[:access_code]
206
      )
207
208
      flash[:success] = I18n.t("room.update_settings_success")
209
    rescue => e
210
      logger.error "Support: Error in updating room settings: #{e}"
211
      flash[:alert] = I18n.t("room.update_settings_error")
212
    end
213
214
    redirect_back fallback_location: room_path(@room)
215
  end
216
217
  # GET /:room_uid/current_presentation
218
  def current_presentation
219
    attached = @room.presentation.attached?
220
221
    # Respond with JSON object of presentation name
222
    respond_to do |format|
223
      format.json { render body: { attached: attached, name: attached ? @room.presentation.filename.to_s : "" }.to_json }
224
    end
225
  end
226
227
  # POST /:room_uid/preupload_presenstation
228
  def preupload_presentation
229
    begin
230
      raise "Invalid file type" unless valid_file_type
231
      @room.presentation.attach(room_params[:presentation])
232
233
      flash[:success] = I18n.t("room.preupload_success")
234
    rescue => e
235
      logger.error "Support: Error in updating room presentation: #{e}"
236
      flash[:alert] = I18n.t("room.preupload_error")
237
    end
238
239
    redirect_back fallback_location: room_path(@room)
240
  end
241
242
  # POST /:room_uid/remove_presenstation
243
  def remove_presentation
244
    begin
245
      @room.presentation.purge
246
247
      flash[:success] = I18n.t("room.preupload_remove_success")
248
    rescue => e
249
      logger.error "Support: Error in removing room presentation: #{e}"
250
      flash[:alert] = I18n.t("room.preupload_remove_error")
251
    end
252
253
    redirect_back fallback_location: room_path(@room)
254
  end
255
256
  # POST /:room_uid/update_shared_access
257
  def shared_access
258
    begin
259
      current_list = @room.shared_users.pluck(:id)
260
      new_list = User.where(uid: params[:add]).pluck(:id)
261
262
      # Get the list of users that used to be in the list but were removed
263
      users_to_remove = current_list - new_list
264
      # Get the list of users that are in the new list but not in the current list
265
      users_to_add = new_list - current_list
266
267
      # Remove users that are removed
268
      SharedAccess.where(room_id: @room.id, user_id: users_to_remove).delete_all unless users_to_remove.empty?
269
270
      # Add users that are added
271
      users_to_add.each do |id|
272
        SharedAccess.create(room_id: @room.id, user_id: id)
273
      end
274
275
      flash[:success] = I18n.t("room.shared_access_success")
276
    rescue => e
277
      logger.error "Support: Error in updating room shared access: #{e}"
278
      flash[:alert] = I18n.t("room.shared_access_error")
279
    end
280
281
    redirect_back fallback_location: room_path
282
  end
283
284
  # POST /:room_uid/remove_shared_access
285
  def remove_shared_access
286
    begin
287
      SharedAccess.find_by!(room_id: @room.id, user_id: current_user).destroy
288
      flash[:success] = I18n.t("room.remove_shared_access_success")
289
    rescue => e
290
      logger.error "Support: Error in removing room shared access: #{e}"
291
      flash[:alert] = I18n.t("room.remove_shared_access_error")
292
    end
293
294
    redirect_to current_user.main_room
295
  end
296
297
  # GET /:room_uid/shared_users
298
  def shared_users
299
    # Respond with JSON object of users that have access to the room
300
    respond_to do |format|
301
      format.json { render body: @room.shared_users.to_json }
302
    end
303
  end
304
305
  # GET /:room_uid/room_settings
306
  def room_settings
307
    # Respond with JSON object of the room_settings
308
    respond_to do |format|
309
      format.json { render body: @room.room_settings }
310
    end
311
  end
312
313
  # GET /:room_uid/logout
314
  def logout
315
    logger.info "Support: #{current_user.present? ? current_user.email : 'Guest'} has left room #{@room.uid}"
316
317
    # Redirect the correct page.
318
    redirect_to @room
319
  end
320
321
  # POST /:room_uid/login
322
  def login
323
    session[:access_code] = room_params[:access_code]
324
325
    flash[:alert] = I18n.t("room.access_code_required") if session[:access_code] != @room.access_code
326
327
    redirect_to room_path(@room.uid)
328
  end
329
330
  private
331
332
  def create_room_settings_string(options)
333
    room_settings = {
334
      "muteOnStart": options[:mute_on_join] == "1",
335
      "requireModeratorApproval": options[:require_moderator_approval] == "1",
336
      "anyoneCanStart": options[:anyone_can_start] == "1",
337
      "joinModerator": options[:all_join_moderator] == "1",
338
      "recording": options[:recording] == "1",
339
    }
340
341
    room_settings.to_json
342
  end
343
344
  def room_params
345
    params.require(:room).permit(:name, :auto_join, :mute_on_join, :access_code,
346
      :require_moderator_approval, :anyone_can_start, :all_join_moderator,
347
      :recording, :presentation)
348
  end
349
350
  # Find the room from the uid.
351
  def find_room
352
    @room = Room.includes(:owner).find_by!(uid: params[:room_uid])
353
  end
354
355
  # Ensure the user either owns the room or is an admin of the room owner or the room is shared with him
356
  def verify_room_ownership_or_admin_or_shared
357
    return redirect_to root_path unless @room.owned_by?(current_user) ||
358
                                        room_shared_with_user ||
359
                                        current_user&.admin_of?(@room.owner, "can_manage_rooms_recordings")
360
  end
361
362
  # Ensure the user either owns the room or is an admin of the room owner
363
  def verify_room_ownership_or_admin
364
    return redirect_to root_path if [email protected]_by?(current_user) &&
365
                                    !current_user&.admin_of?(@room.owner, "can_manage_rooms_recordings")
366
  end
367
368
  # Ensure the user owns the room or is allowed to start it
369
  def verify_room_ownership_or_shared
370
   return redirect_to root_path unless @room.owned_by?(current_user) || room_shared_with_user
371
  end
372
373
  def validate_accepted_terms
374
    redirect_to terms_path if current_user && !current_user&.accepted_terms
375
  end
376
377
  def validate_verified_email
378
    redirect_to account_activation_path(digest: current_user.activation_digest) if current_user && !current_user&.activated?
379
  end
380
381
  def verify_room_owner_verified
382
    redirect_to root_path, alert: t("room.unavailable") unless @room.owner.activated?
383
  end
384
385
  # Check to make sure the room owner is not pending or banned
386
  def verify_room_owner_valid
387
    redirect_to root_path, alert: t("room.owner_banned") if @room.owner.has_role?(:pending) || @room.owner.has_role?(:denied)
388
  end
389
390
  def verify_user_not_admin
391
    redirect_to admins_path if current_user&.has_role?(:super_admin)
392
  end
393
394
  def auth_required
395
    @settings.get_value("Room Authentication") == "true" && current_user.nil?
396
  end
397
398
  # Checks if the room is shared with the user and room sharing is enabled
399
  def room_shared_with_user
400
    shared_access_allowed ? @room.shared_with?(current_user) : false
401
  end
402
403
  def room_limit_exceeded
404
    limit = @settings.get_value("Room Limit").to_i
405
406
    # Does not apply to admin or users that aren't signed in
407
    # 15+ option is used as unlimited
408
    return false if current_user&.has_role?(:admin) || limit == 15
409
410
    current_user.rooms.length >= limit
411
  end
412
  helper_method :room_limit_exceeded
413
414
  def record_meeting
415
    # If the require consent setting is checked, then check the room setting, else, set to true
416
    if recording_consent_required?
417
      room_setting_with_config("recording")
418
    else
419
      true
420
    end
421
  end
422
423
  # Checks if the file extension is allowed
424
  def valid_file_type
425
    Rails.configuration.allowed_file_types.split(",")
426
         .include?(File.extname(room_params[:presentation].original_filename.downcase))
427
  end
428
429
  # Gets the room setting based on the option set in the room configuration
430
  def room_setting_with_config(name)
431
    config = case name
432
    when "muteOnStart"
433
      "Room Configuration Mute On Join"
434
    when "requireModeratorApproval"
435
      "Room Configuration Require Moderator"
436
    when "joinModerator"
437
      "Room Configuration All Join Moderator"
438
    when "anyoneCanStart"
439
      "Room Configuration Allow Any Start"
440
    when "recording"
441
      "Room Configuration Recording"
442
    end
443
444
    case @settings.get_value(config)
445
    when "enabled"
446
      true
447
    when "optional"
448
      @room_settings[name]
449
    when "disabled"
450
      false
451
    end
452
  end
453
  helper_method :room_setting_with_config
454
end
455