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.



215
216
217
# File 'app/models/user.rb', line 215

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.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'app/models/user.rb', line 188

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



174
175
176
177
178
179
180
181
# File 'app/models/user.rb', line 174

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



85
86
87
88
89
90
91
92
93
# File 'app/models/user.rb', line 85

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



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

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
# File 'app/models/user.rb', line 42

def self.from_entra(access_token)
  user = User.find_by(email: access_token.info.email) # uid from entra is not the same as the netid, so we cannot use it to find the user. We need to find the user by email instead.
  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)


105
106
107
# File 'app/models/user.rb', line 105

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.



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

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.



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

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



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/models/user.rb', line 151

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.



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

def self.new_from_entra(access_token)
  user = User.new
  user.provider = access_token.provider
  user.uid = safe_uid(access_token.uid) # this is a long token string, not the netid. How do we handle this?
  user.email = User.email_from_access_token_entra(access_token)
  user.given_name = access_token.info.first_name || access_token.uid # Harriet
  user.family_name = access_token.info.last_name || access_token.uid # Tubman
  user.full_name = access_token.info.name || access_token.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



143
144
145
146
147
148
# File 'app/models/user.rb', line 143

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



80
81
82
83
# File 'app/models/user.rb', line 80

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

.update_super_admins ⇒ Object



206
207
208
209
210
211
# File 'app/models/user.rb', line 206

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



279
280
281
282
283
284
285
# File 'app/models/user.rb', line 279

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



295
296
297
298
299
# File 'app/models/user.rb', line 295

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)


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

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)


258
259
260
261
# File 'app/models/user.rb', line 258

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.



249
250
251
252
253
254
255
# File 'app/models/user.rb', line 249

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)


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

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).



240
241
242
# File 'app/models/user.rb', line 240

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).



233
234
235
# File 'app/models/user.rb', line 233

def given_name_safe
  given_name.presence || uid
end

#moderator? ⇒ Boolean

Returns:

  • (Boolean)


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

def moderator?
  admin_groups.count > 0
end

#pending_notifications_count ⇒ Object



291
292
293
# File 'app/models/user.rb', line 291

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

#safe_uid_check ⇒ Object



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

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



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

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



287
288
289
# File 'app/models/user.rb', line 287

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)


223
224
225
226
227
228
# File 'app/models/user.rb', line 223

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).



112
113
114
115
116
117
118
119
120
# File 'app/models/user.rb', line 112

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



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

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