Class: User

Inherits:
ApplicationRecord show all
Extended by:
FriendlyId
Defined in:
app/models/user.rb

Overview

rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#just_created ⇒ Object

Returns the value of attribute just_created.



23
24
25
# File 'app/models/user.rb', line 23

def just_created
  @just_created
end

Class Method Details

.all_uids_string ⇒ Object

Returns a string with the UID (netid) for all the users. We use this string to power the JavaScript @mention functionality when adding messages to works.



227
228
229
# File 'app/models/user.rb', line 227

def self.all_uids_string
  User.all.map { |user| '"' + user.uid + '"' }.join(", ")
end

.create_default_users ⇒ Object

Creates the default users as indicated in the super_admin config file and the default administrators and submitters for each group. It only creates missing records, i.e. if the records already exist it will not create a duplicate. It also does not remove already configured access to other groups.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'app/models/user.rb', line 200

def self.create_default_users
  update_super_admins

  Group.find_each do |group|
    Rails.logger.info "Setting up admins for group #{group.title}"
    group.default_admins_list.each do |uid|
      user = User.new_for_uid(uid)
      user.add_role :group_admin, group
    end

    Rails.logger.info "Setting up submitters for group #{group.title}"
    group.default_submitters_list.each do |uid|
      user = User.new_for_uid(uid)
      user.add_role :submitter, group
    end
  end
end

.create_users_from_csv(csv) ⇒ Object

rubocop:enable Metrics/MethodLength



186
187
188
189
190
191
192
193
# File 'app/models/user.rb', line 186

def self.create_users_from_csv(csv)
  users = []
  CSV.foreach(csv, headers: true) do |row|
    next if row["Net ID"] == "N/A"
    users << new_from_csv_params(row.to_hash)
  end
  users
end

.email_from_access_token(access_token) ⇒ Object



97
98
99
100
101
102
103
104
105
# File 'app/models/user.rb', line 97

def self.email_from_access_token(access_token)
  if !access_token.extra.mail.nil?
    # For typical Princeton accounts the email comes on the `email` field
    access_token.extra.mail
  elsif User.looks_like_email_address?(access_token.extra.givenname)
    # For Guest Access Accounts (GAP) the email comes in the `givenname`
    access_token.extra.givenname
  end
end

.email_from_access_token_entra(access_token) ⇒ Object



107
108
109
110
111
112
113
114
115
# File 'app/models/user.rb', line 107

def self.email_from_access_token_entra(access_token)
  if !access_token.info.email.nil?
    # For Entra accounts the email comes on the `info.email` field
    access_token.info.email
  elsif User.looks_like_email_address?(access_token.extra.raw_info.preferred_username)
    # For Entra Guest Access Accounts (GAP) the email comes in the `info.given_name`
    access_token.extra.raw_info.preferred_username
  end
end

.from_cas(access_token) ⇒ Object



32
33
34
35
36
37
38
39
40
# File 'app/models/user.rb', line 32

def self.from_cas(access_token)
  user = User.find_by(uid: safe_uid(access_token.uid))
  if user.nil?
    user = new_from_cas(access_token)
  elsif user.provider.blank?
    user.update_with_cas(access_token)
  end
  user
end

.from_entra(access_token) ⇒ Object



42
43
44
45
46
47
48
49
50
51
# File 'app/models/user.rb', line 42

def self.from_entra(access_token)
  uid = uid_from_access_token(access_token)
  user = User.find_by(uid:)
  if user.nil?
    user = new_from_entra(access_token)
  elsif user.provider.blank?
    user.update_with_entra(access_token)
  end
  user
end

.looks_like_email_address?(value) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
# File 'app/models/user.rb', line 117

def self.looks_like_email_address?(value)
  URI::MailTo::EMAIL_REGEXP.match?(value)
end

.new_for_uid(uid, default_group_id = nil) ⇒ Object

Creates a new user by uid. If the user already exists it returns the existing user.



145
146
147
148
149
150
151
152
153
# File 'app/models/user.rb', line 145

def self.new_for_uid(uid, default_group_id = nil)
  user = User.find_by(uid:)
  if user.nil?
    user = User.new(uid:, email: "#{uid}@princeton.edu")
    user.default_group_id = default_group_id
    user.save!
  end
  user
end

.new_from_cas(access_token) ⇒ Object

Create a new user with some basic information from CAS.



54
55
56
57
58
59
60
61
62
63
64
65
# File 'app/models/user.rb', line 54

def self.new_from_cas(access_token)
  user = User.new
  user.provider = access_token.provider
  user.uid = safe_uid(access_token.uid) # this is the netid
  user.email = User.email_from_access_token(access_token)
  user.given_name = access_token.extra.givenname || access_token.uid # Harriet
  user.family_name = access_token.extra.sn || access_token.uid # Tubman
  user.full_name = access_token.extra.displayname || access_token.uid # "Harriet Tubman"
  user.default_group_id = Group.default_for_department(access_token.extra.departmentnumber)&.id
  user.save!
  user
end

.new_from_csv_params(csv_params) ⇒ Object

rubocop:disable Metrics/MethodLength



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'app/models/user.rb', line 163

def self.new_from_csv_params(csv_params)
  email = "#{csv_params['Net ID']}@princeton.edu"
  uid = csv_params["Net ID"]
  given_name = csv_params["First Name"]
  family_name = csv_params["Last Name"]
  full_name = "#{given_name} #{family_name}"
  orcid = csv_params["ORCID ID"]
  user = User.where(email:).first_or_create
  params_hash = {
    email:,
    uid:,
    orcid:,
    full_name: (full_name if user.full_name.blank?),
    family_name: (family_name if user.family_name.blank?),
    given_name: (given_name if user.given_name.blank?)
  }.compact

  user.update(params_hash)
  Rails.logger.info "Successfully created or updated #{user.email}"
  user
end

.new_from_entra(access_token) ⇒ Object

Create a new user with some basic information from Entra.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'app/models/user.rb', line 68

def self.new_from_entra(access_token)
  user = User.new
  email = email_from_access_token_entra(access_token)
  uid = uid_from_access_token(access_token)
  user.provider = access_token.provider
  user.uid = uid
  user.email = email
  user.given_name = access_token.info.first_name || uid # Harriet
  user.family_name = access_token.info.last_name || uid # Tubman
  user.full_name = access_token.info.name || uid # "Harriet Tubman"
  # user.default_group_id = Group.default_for_department(access_token.extra.departmentnumber)&.id # Department number is not available in Entra, so we cannot set a default group based on that.
  user.save!
  user
end

.new_super_admin(uid) ⇒ Object



155
156
157
158
159
160
# File 'app/models/user.rb', line 155

def self.new_super_admin(uid)
  user = new_for_uid(uid)
  user.add_role(:super_admin) unless user.has_role?(:super_admin)
  user.add_role(:group_admin) unless user.has_role?(:group_admin)
  user
end

.safe_uid(uid) ⇒ Object



92
93
94
95
# File 'app/models/user.rb', line 92

def self.safe_uid(uid)
  return uid if uid.blank?
  uid.gsub(/[^(0-9a-zA-Z_\-)]/, "_")
end

.uid_from_access_token(access_token) ⇒ Object



83
84
85
86
87
88
89
90
# File 'app/models/user.rb', line 83

def self.uid_from_access_token(access_token)
  unique_name = access_token.extra.raw_info.unique_name
  if unique_name.end_with?("@princeton.edu") && unique_name.split("@princeton.edu").first.present?
    safe_uid(unique_name.split("@princeton.edu").first)
  else
    safe_uid(unique_name)
  end
end

.update_super_admins ⇒ Object



218
219
220
221
222
223
# File 'app/models/user.rb', line 218

def self.update_super_admins
  Rails.logger.info "Setting super administrators"
  Rails.configuration.super_admins.each do |uid|
    new_super_admin(uid)
  end
end

Instance Method Details

#admin_groups ⇒ Object

Returns the list of groups where the user is an administrator



291
292
293
294
295
296
297
# File 'app/models/user.rb', line 291

def admin_groups
  @admin_groups ||= if super_admin?
                      Group.all.to_a
                    else
                      Group.with_role(:group_admin, self)
                    end
end

#assign_default_role ⇒ Object



307
308
309
310
311
# File 'app/models/user.rb', line 307

def assign_default_role
  @just_created = true
  add_role(:submitter, default_group) unless has_role?(:submitter, default_group)
  default_group.enable_messages_for(user: self)
end

#can_admin?(group) ⇒ Boolean

Returns true if the user can admin the group

Returns:

  • (Boolean)


276
277
278
279
# File 'app/models/user.rb', line 276

def can_admin?(group)
  return true if super_admin?
  has_role?(:group_admin, group)
end

#can_submit?(group) ⇒ Boolean

True if the user can submit datasets to the group

Returns:

  • (Boolean)


270
271
272
273
# File 'app/models/user.rb', line 270

def can_submit?(group)
  return true if super_admin?
  has_role?(:submitter, group)
end

#default_group ⇒ Object

Returns a reference to the user's default group.



261
262
263
264
265
266
267
# File 'app/models/user.rb', line 261

def default_group
  if default_group_id.nil?
    Group.default
  else
    Group.find(default_group_id)
  end
end

#email_messages_enabled? ⇒ Boolean

Returns true if the user has notification e-mails enabled

Returns:

  • (Boolean)


315
316
317
# File 'app/models/user.rb', line 315

def email_messages_enabled?
  email_messages_enabled
end

#full_name_safe ⇒ Object

Returns a full name that always has a value This is needed because we have records in the Users table that are created automatically in which the only value we have for sure its their uid (aka NetID).



252
253
254
# File 'app/models/user.rb', line 252

def full_name_safe
  full_name&.strip.presence || uid
end

#given_name_safe ⇒ Object

Returns a display name that always has a value This is needed because we have records in the Users table that are created automatically in which the only value we have for sure its their uid (aka NetID).



245
246
247
# File 'app/models/user.rb', line 245

def given_name_safe
  given_name.presence || uid
end

#moderator? ⇒ Boolean

Returns:

  • (Boolean)


256
257
258
# File 'app/models/user.rb', line 256

def moderator?
  admin_groups.count > 0
end

#pending_notifications_count ⇒ Object



303
304
305
# File 'app/models/user.rb', line 303

def pending_notifications_count
  WorkActivityNotification.where(user_id: id, read_at: nil).count
end

#safe_uid_check ⇒ Object



319
320
321
# File 'app/models/user.rb', line 319

def safe_uid_check
  self.uid = self.class.safe_uid(uid)
end

#submitter_groups ⇒ Object

Returns the list of groups where the user can submit datasets



282
283
284
285
286
287
288
# File 'app/models/user.rb', line 282

def submitter_groups
  @submitter_groups = if super_admin?
                        Group.all.to_a
                      else
                        (Group.with_role(:submitter, self) + Group.with_role(:group_admin, self)).uniq
                      end
end

#submitter_or_admin_groups ⇒ Object



299
300
301
# File 'app/models/user.rb', line 299

def submitter_or_admin_groups
  submitter_groups | admin_groups
end

#super_admin? ⇒ Boolean

Is this user a super_admin? super_admins automatically get admin status in every group, and they can make new groups.

Returns:

  • (Boolean)


235
236
237
238
239
240
# File 'app/models/user.rb', line 235

def super_admin?
  has_role? :super_admin
rescue => ex
  Rails.logger.error("Unexpected error checking super_admin: #{ex}")
  false
end

#update_with_cas(access_token) ⇒ Object

Updates an existing User record with some information from CAS. This is useful for records created before the user ever logged in (e.g. to grant them permissions to groups).



124
125
126
127
128
129
130
131
132
# File 'app/models/user.rb', line 124

def update_with_cas(access_token)
  self.provider = access_token.provider
  self.email = User.email_from_access_token(access_token)
  self.given_name = access_token.extra.givenname || access_token.uid # Harriet
  self.family_name = access_token.extra.sn || access_token.uid # Tubman
  self.full_name = access_token.extra.displayname || access_token.uid # "Harriet Tubman"
  self.default_group_id ||= Group.default_for_department(access_token.extra.departmentnumber)&.id
  save!
end

#update_with_entra(access_token) ⇒ Object



134
135
136
137
138
139
140
141
142
# File 'app/models/user.rb', line 134

def update_with_entra(access_token)
  self.provider = access_token.info.provider
  self.email = User.email_from_access_token_entra(access_token)
  self.given_name = access_token.info.first_name || access_token.uid # Harriet
  self.family_name = access_token.info.last_name || access_token.uid # Tubman
  self.full_name = access_token.info.name || access_token.uid # "Harriet Tubman"
  # self.default_group_id ||= Group.default_for_department(access_token.info.department_number)&.id
  save!
end