1 # frozen_string_literal: true
2 # == Schema Information
6 # id :bigint(8) not null, primary key
7 # email :string default(""), not null
8 # created_at :datetime not null
9 # updated_at :datetime not null
10 # encrypted_password :string default(""), not null
11 # reset_password_token :string
12 # reset_password_sent_at :datetime
13 # remember_created_at :datetime
14 # sign_in_count :integer default(0), not null
15 # current_sign_in_at :datetime
16 # last_sign_in_at :datetime
17 # current_sign_in_ip :inet
18 # last_sign_in_ip :inet
19 # admin :boolean default(FALSE), not null
20 # confirmation_token :string
21 # confirmed_at :datetime
22 # confirmation_sent_at :datetime
23 # unconfirmed_email :string
25 # encrypted_otp_secret :string
26 # encrypted_otp_secret_iv :string
27 # encrypted_otp_secret_salt :string
28 # consumed_timestep :integer
29 # otp_required_for_login :boolean default(FALSE), not null
30 # last_emailed_at :datetime
31 # otp_backup_codes :string is an Array
32 # filtered_languages :string default([]), not null, is an Array
33 # account_id :bigint(8) not null
34 # disabled :boolean default(FALSE), not null
35 # moderator :boolean default(FALSE), not null
36 # invite_id :bigint(8)
37 # remember_token :string
38 # chosen_languages :string is an Array
39 # created_by_application_id :bigint(8)
40 # approved :boolean default(TRUE), not null
43 class User
< ApplicationRecord
44 include Settings
::Extend
47 # The home and list feeds will be stored in Redis for this amount
48 # of time, and status fan-out to followers will include only people
49 # within this time frame. Lowering the duration may improve performance
50 # if lots of people sign up, but not a lot of them check their feed
51 # every day. Raising the duration reduces the amount of expensive
52 # RegenerationWorker jobs that need to be run when those people come
54 ACTIVE_DURATION
= ENV.fetch('USER_ACTIVE_DAYS', 7).to_i
.days
.freeze
56 devise
:two_factor_authenticatable,
57 otp_secret_encryption_key
: Rails
.configuration
.x
.otp_secret
59 devise
:two_factor_backupable,
60 otp_number_of_backup_codes
: 10
62 devise
:registerable, :recoverable, :rememberable, :trackable, :validatable,
66 include PamAuthenticable
67 include LdapAuthenticable
69 belongs_to
:account, inverse_of
: :user
70 belongs_to
:invite, counter_cache
: :uses, optional
: true
71 belongs_to
:created_by_application, class_name
: 'Doorkeeper::Application', optional
: true
72 accepts_nested_attributes_for
:account
74 has_many
:applications, class_name
: 'Doorkeeper::Application', as
: :owner
75 has_many
:backups, inverse_of
: :user
76 has_many
:invites, inverse_of
: :user
77 has_many
:markers, inverse_of
: :user, dependent
: :destroy
79 has_one
:invite_request, class_name
: 'UserInviteRequest', inverse_of
: :user, dependent
: :destroy
80 accepts_nested_attributes_for
:invite_request, reject_if
: ->(attributes
) { attributes
['text'].blank
? }
82 validates
:locale, inclusion
: I18n
.available_locales
.map(&:to_s), if: :locale?
83 validates_with BlacklistedEmailValidator
, on
: :create
84 validates_with EmailMxValidator
, if: :validate_email_dns?
85 validates
:agreement, acceptance
: { allow_nil
: false, accept
: [true, 'true', '1'] }, on
: :create
87 scope
:recent, -> { order(id
: :desc) }
88 scope
:pending, -> { where(approved
: false) }
89 scope
:approved, -> { where(approved
: true) }
90 scope
:confirmed, -> { where
.not(confirmed_at
: nil) }
91 scope
:enabled, -> { where(disabled
: false) }
92 scope
:disabled, -> { where(disabled
: true) }
93 scope
:inactive, -> { where(arel_table
[:current_sign_in_at].lt(ACTIVE_DURATION
.ago
)) }
94 scope
:active, -> { confirmed
.where(arel_table
[:current_sign_in_at].gteq(ACTIVE_DURATION
.ago
)).joins(:account).where(accounts
: { suspended_at
: nil }) }
95 scope
:matches_email, ->(value
) { where(arel_table
[:email].matches("#{value}%")) }
96 scope
:emailable, -> { confirmed
.enabled
.joins(:account).merge(Account
.searchable
) }
98 before_validation
:sanitize_languages
99 before_create
:set_approved
101 # This avoids a deprecation warning from Rails 5.1
102 # It seems possible that a future release of devise-two-factor will
103 # handle this itself, and this can be removed from our User class.
104 attribute
:otp_secret
106 has_many
:session_activations, dependent
: :destroy
108 delegate
:auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
109 :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
110 :expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
111 :advanced_layout, :use_blurhash, :use_pending_items, :trends,
112 to
: :settings, prefix
: :setting, allow_nil
: false
114 attr_reader
:invite_code
115 attr_writer
:external
118 confirmed_at
.present
?
125 def valid_invitation
?
126 invite_id
.present
? && invite
.valid_for_use
?
130 update!
(disabled
: true,
131 last_sign_in_at
: current_sign_in_at
,
132 current_sign_in_at
: nil)
136 update!
(disabled
: false)
140 new_user
= !confirmed
?
141 self.approved
= true if open_registrations
?
145 if new_user
&& approved
?
148 notify_staff_about_pending_account!
153 new_user
= !confirmed
?
154 self.approved
= true if open_registrations
?
159 prepare_new_user!
if new_user
&& approved
?
166 def active_for_authentication
?
171 confirmed
? && approved
? && !disabled
? && !account
.suspended
? && account
.moved_to_account_id
.nil?
174 def unconfirmed_or_pending
?
175 !
(confirmed
? && approved
?)
179 !approved
? ? :pending : super
185 update!
(approved
: true)
189 def update_tracked_fields!
(request
)
191 prepare_returning_user!
194 def disable_two_factor!
195 self.otp_required_for_login
= false
196 otp_backup_codes
&.clear
200 def setting_default_privacy
201 settings
.default_privacy
|| (account
.locked
? ? 'private' : 'public')
204 def allows_digest_emails
?
205 settings
.notification_emails
['digest']
208 def allows_report_emails
?
209 settings
.notification_emails
['report']
212 def allows_pending_account_emails
?
213 settings
.notification_emails
['pending_account']
216 def allows_trending_tag_emails
?
217 settings
.notification_emails
['trending_tag']
221 @hides_network ||= settings
.hide_network
224 def aggregates_reblogs
?
225 @aggregates_reblogs ||= settings
.aggregate_reblogs
228 def shows_application
?
229 @shows_application ||= settings
.show_application
233 return nil if a
.nil? || a
.owner !
= self
234 Doorkeeper
::AccessToken
235 .find_or_create_by(application_id
: a
.id
, resource_owner_id
: id
) do |t
|
238 t
.expires_in
= Doorkeeper
.configuration
.access_token_expires_in
239 t
.use_refresh_token
= Doorkeeper
.configuration
.refresh_token_enabled
?
243 def activate_session(request
)
244 session_activations
.activate(session_id
: SecureRandom
.hex
,
245 user_agent
: request
.user_agent
,
246 ip
: request
.remote_ip
).session_id
249 def exclusive_session(id
)
250 session_activations
.exclusive(id
)
253 def session_active
?(id
)
254 session_activations
.active
? id
257 def web_push_subscription(session
)
258 session
.web_push_subscription
.nil? ? nil : session
.web_push_subscription
261 def invite_code
=(code
)
262 self.invite
= Invite
.find_by(code
: code
) if code
.present
?
266 def password_required
?
267 return false if external
?
272 def send_reset_password_instructions
273 return false if encrypted_password
.blank
?
278 def reset_password!
(new_password
, new_password_confirmation
)
279 return false if encrypted_password
.blank
?
285 setting_display_media
== 'show_all'
289 setting_display_media
== 'hide_all'
294 def send_devise_notification(notification
, *args
)
295 devise_mailer
.send(notification
, self, *args
).deliver_later
301 self.approved
= open_registrations
? || valid_invitation
? || external
?
304 def open_registrations
?
305 Setting
.registrations_mode
== 'open'
312 def sanitize_languages
313 return if chosen_languages
.nil?
314 chosen_languages
.reject!
(&:blank?)
315 self.chosen_languages
= nil if chosen_languages
.empty
?
318 def prepare_new_user!
319 BootstrapTimelineWorker
.perform_async(account_id
)
320 ActivityTracker
.increment('activity:accounts:local')
321 UserMailer
.welcome(self).deliver_later
324 def prepare_returning_user!
325 ActivityTracker
.record('activity:logins', id
)
326 regenerate_feed!
if needs_feed_update
?
329 def notify_staff_about_pending_account!
330 User
.staff
.includes(:account).each
do |u
|
331 next unless u
.allows_pending_account_emails
?
332 AdminMailer
.new_pending_account(u
.account
, self).deliver_later
337 return unless Redis
.current
.setnx("account:#{account_id}:regeneration", true)
338 Redis
.current
.expire("account:#{account_id}:regeneration", 1.day
.seconds
)
339 RegenerationWorker
.perform_async(account_id
)
342 def needs_feed_update
?
343 last_sign_in_at
< ACTIVE_DURATION
.ago
346 def validate_email_dns
?
347 email_changed
? && !
(Rails
.env.test
? || Rails
.env.development
?)