]> cat aescling's git repositories - mastodon.git/blob - app/models/user.rb
Merge remote-tracking branch 'origin/master' into gs-master
[mastodon.git] / app / models / user.rb
1 # frozen_string_literal: true
2 # == Schema Information
3 #
4 # Table name: users
5 #
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
24 # locale :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 #
39
40 class User < ApplicationRecord
41 include Settings::Extend
42 include Omniauthable
43
44 ACTIVE_DURATION = 14.days
45
46 devise :two_factor_authenticatable,
47 otp_secret_encryption_key: Rails.configuration.x.otp_secret
48
49 devise :two_factor_backupable,
50 otp_number_of_backup_codes: 10
51
52 devise :registerable, :recoverable, :rememberable, :trackable, :validatable,
53 :confirmable
54
55 devise :pam_authenticatable if ENV['PAM_ENABLED'] == 'true'
56
57 devise :omniauthable
58
59 belongs_to :account, inverse_of: :user
60 belongs_to :invite, counter_cache: :uses, optional: true
61 accepts_nested_attributes_for :account
62
63 has_many :applications, class_name: 'Doorkeeper::Application', as: :owner
64 has_many :backups, inverse_of: :user
65
66 validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
67 validates_with BlacklistedEmailValidator, if: :email_changed?
68
69 scope :recent, -> { order(id: :desc) }
70 scope :admins, -> { where(admin: true) }
71 scope :moderators, -> { where(moderator: true) }
72 scope :staff, -> { admins.or(moderators) }
73 scope :confirmed, -> { where.not(confirmed_at: nil) }
74 scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
75 scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where(accounts: { suspended: false }) }
76 scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
77 scope :with_recent_ip_address, ->(value) { where(arel_table[:current_sign_in_ip].eq(value).or(arel_table[:last_sign_in_ip].eq(value))) }
78
79 before_validation :sanitize_languages
80
81 # This avoids a deprecation warning from Rails 5.1
82 # It seems possible that a future release of devise-two-factor will
83 # handle this itself, and this can be removed from our User class.
84 attribute :otp_secret
85
86 has_many :session_activations, dependent: :destroy
87
88 delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
89 :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_sensitive_media,
90 to: :settings, prefix: :setting, allow_nil: false
91
92 attr_accessor :invite_code
93
94 def pam_conflict(_)
95 # block pam login tries on traditional account
96 nil
97 end
98
99 def pam_conflict?
100 return false unless Devise.pam_authentication
101 encrypted_password.present? && pam_managed_user?
102 end
103
104 def pam_get_name
105 return account.username if account.present?
106 super
107 end
108
109 def pam_setup(_attributes)
110 acc = Account.new(username: pam_get_name)
111 acc.save!(validate: false)
112
113 self.email = "#{acc.username}@#{find_pam_suffix}" if email.nil? && find_pam_suffix
114 self.confirmed_at = Time.now.utc
115 self.admin = false
116 self.account = acc
117
118 acc.destroy! unless save
119 end
120
121 def ldap_setup(_attributes)
122 self.confirmed_at = Time.now.utc
123 self.admin = false
124 save!
125 end
126
127 def confirmed?
128 confirmed_at.present?
129 end
130
131 def staff?
132 admin? || moderator?
133 end
134
135 def role
136 if admin?
137 'admin'
138 elsif moderator?
139 'moderator'
140 else
141 'user'
142 end
143 end
144
145 def role?(role)
146 case role
147 when 'user'
148 true
149 when 'moderator'
150 staff?
151 when 'admin'
152 admin?
153 else
154 false
155 end
156 end
157
158 def disable!
159 update!(disabled: true,
160 last_sign_in_at: current_sign_in_at,
161 current_sign_in_at: nil)
162 end
163
164 def enable!
165 update!(disabled: false)
166 end
167
168 def confirm
169 new_user = !confirmed?
170
171 super
172 prepare_new_user! if new_user
173 end
174
175 def confirm!
176 new_user = !confirmed?
177
178 skip_confirmation!
179 save!
180 prepare_new_user! if new_user
181 end
182
183 def update_tracked_fields!(request)
184 super
185 prepare_returning_user!
186 end
187
188 def promote!
189 if moderator?
190 update!(moderator: false, admin: true)
191 elsif !admin?
192 update!(moderator: true)
193 end
194 end
195
196 def demote!
197 if admin?
198 update!(admin: false, moderator: true)
199 elsif moderator?
200 update!(moderator: false)
201 end
202 end
203
204 def disable_two_factor!
205 self.otp_required_for_login = false
206 otp_backup_codes&.clear
207 save!
208 end
209
210 def active_for_authentication?
211 super && !disabled?
212 end
213
214 def setting_default_privacy
215 settings.default_privacy || (account.locked? ? 'private' : 'public')
216 end
217
218 def allows_digest_emails?
219 settings.notification_emails['digest']
220 end
221
222 def token_for_app(a)
223 return nil if a.nil? || a.owner != self
224 Doorkeeper::AccessToken
225 .find_or_create_by(application_id: a.id, resource_owner_id: id) do |t|
226
227 t.scopes = a.scopes
228 t.expires_in = Doorkeeper.configuration.access_token_expires_in
229 t.use_refresh_token = Doorkeeper.configuration.refresh_token_enabled?
230 end
231 end
232
233 def activate_session(request)
234 session_activations.activate(session_id: SecureRandom.hex,
235 user_agent: request.user_agent,
236 ip: request.remote_ip).session_id
237 end
238
239 def exclusive_session(id)
240 session_activations.exclusive(id)
241 end
242
243 def session_active?(id)
244 session_activations.active? id
245 end
246
247 def web_push_subscription(session)
248 session.web_push_subscription.nil? ? nil : session.web_push_subscription.as_payload
249 end
250
251 def invite_code=(code)
252 self.invite = Invite.find_by(code: code) unless code.blank?
253 @invite_code = code
254 end
255
256 def password_required?
257 return false if Devise.pam_authentication || Devise.ldap_authentication
258 super
259 end
260
261 def send_reset_password_instructions
262 return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication)
263 super
264 end
265
266 def reset_password!(new_password, new_password_confirmation)
267 return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication)
268 super
269 end
270
271 def self.pam_get_user(attributes = {})
272 return nil unless attributes[:email]
273 resource =
274 if Devise.check_at_sign && !attributes[:email].index('@')
275 joins(:account).find_by(accounts: { username: attributes[:email] })
276 else
277 find_by(email: attributes[:email])
278 end
279
280 if resource.blank?
281 resource = new(email: attributes[:email])
282 if Devise.check_at_sign && !resource[:email].index('@')
283 resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
284 resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
285 end
286 end
287 resource
288 end
289
290 def self.ldap_get_user(attributes = {})
291 resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first })
292
293 if resource.blank?
294 resource = new(email: attributes[:mail].first, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first })
295 resource.ldap_setup(attributes)
296 end
297
298 resource
299 end
300
301 def self.authenticate_with_pam(attributes = {})
302 return nil unless Devise.pam_authentication
303 super
304 end
305
306 protected
307
308 def send_devise_notification(notification, *args)
309 devise_mailer.send(notification, self, *args).deliver_later
310 end
311
312 private
313
314 def sanitize_languages
315 filtered_languages.reject!(&:blank?)
316 end
317
318 def prepare_new_user!
319 BootstrapTimelineWorker.perform_async(account_id)
320 ActivityTracker.increment('activity:accounts:local')
321 UserMailer.welcome(self).deliver_later
322 end
323
324 def prepare_returning_user!
325 ActivityTracker.record('activity:logins', id)
326 regenerate_feed! if needs_feed_update?
327 end
328
329 def regenerate_feed!
330 Redis.current.setnx("account:#{account_id}:regeneration", true) && Redis.current.expire("account:#{account_id}:regeneration", 1.day.seconds)
331 RegenerationWorker.perform_async(account_id)
332 end
333
334 def needs_feed_update?
335 last_sign_in_at < ACTIVE_DURATION.ago
336 end
337 end
This page took 0.18337 seconds and 4 git commands to generate.