Passed
Push — master ( e42d2d...6d6ac3 )
by Jesus
04:57
created

User.add_role()   B

Complexity

Conditions 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
dl 0
loc 17
rs 8.6166
c 0
b 0
f 0
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
require 'bbb_api'
20
21
class User < ApplicationRecord
22
  include ::BbbApi
23
24
  attr_accessor :reset_token
25
  after_create :assign_default_role
26
  after_create :initialize_main_room
27
28
  before_save { email.try(:downcase!) }
29
30
  before_destroy :destroy_rooms
31
32
  has_many :rooms
33
  belongs_to :main_room, class_name: 'Room', foreign_key: :room_id, required: false
34
35
  has_and_belongs_to_many :roles, join_table: :users_roles
36
37
  validates :name, length: { maximum: 256 }, presence: true
38
  validates :provider, presence: true
39
  validate :check_if_email_can_be_blank
40
  validates :email, length: { maximum: 256 }, allow_blank: true,
41
                    uniqueness: { case_sensitive: false, scope: :provider },
42
                    format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
43
44
  validates :password, length: { minimum: 6 }, confirmation: true, if: :greenlight_account?, on: :create
45
46
  # Bypass validation if omniauth
47
  validates :accepted_terms, acceptance: true,
48
                             unless: -> { !greenlight_account? || !Rails.configuration.terms }
49
50
  # We don't want to require password validations on all accounts.
51
  has_secure_password(validations: false)
52
53
  class << self
54
    # Generates a user from omniauth.
55
    def from_omniauth(auth)
56
      # Provider is the customer name if in loadbalanced config mode
57
      provider = auth['provider'] == "bn_launcher" ? auth['info']['customer'] : auth['provider']
58
      find_or_initialize_by(social_uid: auth['uid'], provider: provider).tap do |u|
59
        u.name = auth_name(auth) unless u.name
60
        u.username = auth_username(auth) unless u.username
61
        u.email = auth_email(auth)
62
        u.image = auth_image(auth) unless u.image
63
        auth_roles(u, auth)
64
        u.email_verified = true
65
        u.save!
66
      end
67
    end
68
69
    private
70
71
    # Provider attributes.
72
    def auth_name(auth)
73
      case auth['provider']
74
      when :office365
75
        auth['info']['display_name']
76
      else
77
        auth['info']['name']
78
      end
79
    end
80
81
    def auth_username(auth)
82
      case auth['provider']
83
      when :google
84
        auth['info']['email'].split('@').first
85
      when :bn_launcher
86
        auth['info']['username']
87
      else
88
        auth['info']['nickname']
89
      end
90
    end
91
92
    def auth_email(auth)
93
      auth['info']['email']
94
    end
95
96
    def auth_image(auth)
97
      case auth['provider']
98
      when :twitter
99
        auth['info']['image'].gsub("http", "https").gsub("_normal", "")
100
      else
101
        auth['info']['image']
102
      end
103
    end
104
105
    def auth_roles(user, auth)
106
      unless auth['info']['roles'].nil?
107
        roles = auth['info']['roles'].split(',')
108
109
        role_provider = auth['provider'] == "bn_launcher" ? auth['info']['customer'] : "greenlight"
110
        roles.each do |role_name|
111
          role = Role.where(provider: role_provider, name: role_name).first
112
          user.roles << role unless role.nil?
113
        end
114
      end
115
    end
116
  end
117
118
  def self.admins_search(string, role)
119
    active_database = Rails.configuration.database_configuration[Rails.env]["adapter"]
120
    # Postgres requires created_at to be cast to a string
121
    created_at_query = if active_database == "postgresql"
122
      "created_at::text"
123
    else
124
      "created_at"
125
    end
126
127
    search_query = ""
128
    role_search_param = ""
129
    if role.nil?
130
      search_query = "users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
131
                    " OR users.#{created_at_query} LIKE :search OR users.provider LIKE :search" \
132
                    " OR roles.name LIKE :roles_search"
133
      role_search_param = "%#{string}%"
134
    else
135
      search_query = "(users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
136
                    " OR users.#{created_at_query} LIKE :search OR users.provider LIKE :search)" \
137
                    " AND roles.name = :roles_search"
138
      role_search_param = role.name
139
    end
140
141
    search_param = "%#{string}%"
142
    joins("LEFT OUTER JOIN users_roles ON users_roles.user_id = users.id LEFT OUTER JOIN roles " \
143
      "ON roles.id = users_roles.role_id").distinct
144
      .where(search_query, search: search_param, roles_search: role_search_param)
145
  end
146
147
  def self.admins_order(column, direction)
148
    # Arel.sql to avoid sql injection
149
    order(Arel.sql("#{column} #{direction}"))
150
  end
151
152
  # Activates an account and initialize a users main room
153
  def activate
154
    update_attribute(:email_verified, true)
155
    update_attribute(:activated_at, Time.zone.now)
156
    save
157
  end
158
159
  def activated?
160
    return true unless Rails.configuration.enable_email_verification
161
    email_verified
162
  end
163
164
  # Sets the password reset attributes.
165
  def create_reset_digest
166
    self.reset_token = User.new_token
167
    update_attribute(:reset_digest,  User.digest(reset_token))
168
    update_attribute(:reset_sent_at, Time.zone.now)
169
  end
170
171
  # Returns true if the given token matches the digest.
172
  def authenticated?(attribute, token)
173
    digest = send("#{attribute}_digest")
174
    return false if digest.nil?
175
    BCrypt::Password.new(digest).is_password?(token)
176
  end
177
178
  # Return true if password reset link expires
179
  def password_reset_expired?
180
    reset_sent_at < 2.hours.ago
181
  end
182
183
  # Retrives a list of all a users rooms that are not the main room, sorted by last session date.
184
  def secondary_rooms
185
    secondary = (rooms - [main_room])
186
    no_session, session = secondary.partition { |r| r.last_session.nil? }
187
    sorted = session.sort_by(&:last_session)
188
    sorted + no_session
189
  end
190
191
  def name_chunk
192
    charset = ("a".."z").to_a - %w(b i l o s) + ("2".."9").to_a - %w(5 8)
193
    chunk = name.parameterize[0...3]
194
    if chunk.empty?
195
      chunk + (0...3).map { charset.to_a[rand(charset.size)] }.join
196
    elsif chunk.length == 1
197
      chunk + (0...2).map { charset.to_a[rand(charset.size)] }.join
198
    elsif chunk.length == 2
199
      chunk + (0...1).map { charset.to_a[rand(charset.size)] }.join
200
    else
201
      chunk
202
    end
203
  end
204
205
  def greenlight_account?
206
    return true unless provider # For testing cases when provider is set to null
207
    return true if provider == "greenlight"
208
    return false unless Rails.configuration.loadbalanced_configuration
209
    # Proceed with fetching the provider info
210
    provider_info = retrieve_provider_info(provider, 'api2', 'getUserGreenlightCredentials')
211
    provider_info['provider'] == 'greenlight'
212
  end
213
214
  def activation_token
215
    # Create the token.
216
    create_reset_activation_digest(User.new_token)
217
  end
218
219
  def admin_of?(user)
220
    if Rails.configuration.loadbalanced_configuration
221
      if has_role? :super_admin
222
        id != user.id
223
      else
224
        highest_priority_role.can_manage_users && (id != user.id) && (provider == user.provider) &&
225
          (!user.has_role? :super_admin)
226
      end
227
    else
228
      (highest_priority_role.can_manage_users || (has_role? :super_admin)) && (id != user.id)
229
    end
230
  end
231
232
  def self.digest(string)
233
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
234
    BCrypt::Password.create(string, cost: cost)
235
  end
236
237
  # Returns a random token.
238
  def self.new_token
239
    SecureRandom.urlsafe_base64
240
  end
241
242
  # role functions
243
  def highest_priority_role
244
    roles.by_priority.first
245
  end
246
247
  def add_role(role)
248
    unless has_role?(role)
249
      role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
250
251
      new_role = Role.find_by(name: role, provider: role_provider)
252
253
      if new_role.nil?
254
        return if Role.duplicate_name(role, role_provider) || role.strip.empty?
255
256
        new_role = Role.create_new_role(role, role_provider)
257
      end
258
259
      roles << new_role
260
261
      save!
262
    end
263
  end
264
265
  def remove_role(role)
266
    if has_role?(role)
267
      role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
268
269
      roles.delete(Role.find_by(name: role, provider: role_provider))
270
      save!
271
    end
272
  end
273
274
  # This rule is disabled as the function name must be has_role?
275
  # rubocop:disable Naming/PredicateName
276
  def has_role?(role)
277
    # rubocop:enable Naming/PredicateName
278
    roles.exists?(name: role)
279
  end
280
281
  def self.with_role(role)
282
    User.all_users_with_roles.where(roles: { name: role })
283
  end
284
285
  def self.without_role(role)
286
    User.where.not(id: with_role(role).pluck(:id))
287
  end
288
289
  def self.all_users_with_roles
290
    User.joins("INNER JOIN users_roles ON users_roles.user_id = users.id INNER JOIN roles " \
291
      "ON roles.id = users_roles.role_id")
292
  end
293
294
  private
295
296
  def create_reset_activation_digest(token)
297
    # Create the digest and persist it.
298
    self.activation_digest = User.digest(token)
299
    save
300
    token
301
  end
302
303
  # Destory a users rooms when they are removed.
304
  def destroy_rooms
305
    rooms.destroy_all
306
  end
307
308
  # Initializes a room for the user and assign a BigBlueButton user id.
309
  def initialize_main_room
310
    self.uid = "gl-#{(0...12).map { rand(65..90).chr }.join.downcase}"
311
    self.main_room = Room.create!(owner: self, name: I18n.t("home_room"))
312
    save
313
  end
314
315
  # Initialize the user to use the default user role
316
  def assign_default_role
317
    role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
318
319
    Role.create_default_roles(role_provider) if Role.where(provider: role_provider).count.zero?
320
321
    add_role(:user) if roles.blank?
322
  end
323
324
  def check_if_email_can_be_blank
325
    if email.blank?
326
      if Rails.configuration.loadbalanced_configuration && greenlight_account?
327
        errors.add(:email, I18n.t("errors.messages.blank"))
328
      elsif provider == "greenlight"
329
        errors.add(:email, I18n.t("errors.messages.blank"))
330
      end
331
    end
332
  end
333
end
334