account.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. # frozen_string_literal: true
  2. # == Schema Information
  3. #
  4. # Table name: accounts
  5. #
  6. # id :bigint(8) not null, primary key
  7. # username :string default(""), not null
  8. # domain :string
  9. # private_key :text
  10. # public_key :text default(""), not null
  11. # created_at :datetime not null
  12. # updated_at :datetime not null
  13. # note :text default(""), not null
  14. # display_name :string default(""), not null
  15. # uri :string default(""), not null
  16. # url :string
  17. # avatar_file_name :string
  18. # avatar_content_type :string
  19. # avatar_file_size :integer
  20. # avatar_updated_at :datetime
  21. # header_file_name :string
  22. # header_content_type :string
  23. # header_file_size :integer
  24. # header_updated_at :datetime
  25. # avatar_remote_url :string
  26. # locked :boolean default(FALSE), not null
  27. # header_remote_url :string default(""), not null
  28. # last_webfingered_at :datetime
  29. # inbox_url :string default(""), not null
  30. # outbox_url :string default(""), not null
  31. # shared_inbox_url :string default(""), not null
  32. # followers_url :string default(""), not null
  33. # protocol :integer default("ostatus"), not null
  34. # memorial :boolean default(FALSE), not null
  35. # moved_to_account_id :bigint(8)
  36. # featured_collection_url :string
  37. # fields :jsonb
  38. # actor_type :string
  39. # discoverable :boolean
  40. # also_known_as :string is an Array
  41. # silenced_at :datetime
  42. # suspended_at :datetime
  43. # hide_collections :boolean
  44. # avatar_storage_schema_version :integer
  45. # header_storage_schema_version :integer
  46. # devices_url :string
  47. # suspension_origin :integer
  48. # sensitized_at :datetime
  49. # trendable :boolean
  50. # reviewed_at :datetime
  51. # requested_review_at :datetime
  52. # indexable :boolean default(FALSE), not null
  53. #
  54. class Account < ApplicationRecord
  55. self.ignored_columns += %w(
  56. subscription_expires_at
  57. secret
  58. remote_url
  59. salmon_url
  60. hub_url
  61. trust_level
  62. )
  63. BACKGROUND_REFRESH_INTERVAL = 1.week.freeze
  64. USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i
  65. MENTION_RE = %r{(?<=^|[^/[:word:]])@((#{USERNAME_RE})(?:@[[:word:].-]+[[:word:]]+)?)}i
  66. URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+}
  67. USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i
  68. include Attachmentable
  69. include AccountAssociations
  70. include AccountAvatar
  71. include AccountFinderConcern
  72. include AccountHeader
  73. include AccountInteractions
  74. include Paginable
  75. include AccountCounters
  76. include DomainNormalizable
  77. include DomainMaterializable
  78. include AccountMerging
  79. include AccountSearch
  80. include AccountStatusesSearch
  81. enum protocol: { ostatus: 0, activitypub: 1 }
  82. enum suspension_origin: { local: 0, remote: 1 }, _prefix: true
  83. validates :username, presence: true
  84. validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? }
  85. # Remote user validations, also applies to internal actors
  86. validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (!local? || actor_type == 'Application') && will_save_change_to_username? }
  87. # Remote user validations
  88. validates :uri, presence: true, unless: :local?, on: :create
  89. # Local user validations
  90. validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
  91. validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
  92. validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
  93. validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? }
  94. validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
  95. validates :uri, absence: true, if: :local?, on: :create
  96. validates :inbox_url, absence: true, if: :local?, on: :create
  97. validates :shared_inbox_url, absence: true, if: :local?, on: :create
  98. validates :followers_url, absence: true, if: :local?, on: :create
  99. scope :remote, -> { where.not(domain: nil) }
  100. scope :local, -> { where(domain: nil) }
  101. scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
  102. scope :silenced, -> { where.not(silenced_at: nil) }
  103. scope :suspended, -> { where.not(suspended_at: nil) }
  104. scope :sensitized, -> { where.not(sensitized_at: nil) }
  105. scope :without_suspended, -> { where(suspended_at: nil) }
  106. scope :without_silenced, -> { where(silenced_at: nil) }
  107. scope :without_instance_actor, -> { where.not(id: -99) }
  108. scope :recent, -> { reorder(id: :desc) }
  109. scope :bots, -> { where(actor_type: %w(Application Service)) }
  110. scope :groups, -> { where(actor_type: 'Group') }
  111. scope :alphabetic, -> { order(domain: :asc, username: :asc) }
  112. scope :matches_username, ->(value) { where('lower((username)::text) LIKE lower(?)', "#{value}%") }
  113. scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
  114. scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
  115. scope :without_unapproved, -> { left_outer_joins(:user).merge(User.approved.confirmed).or(remote) }
  116. scope :searchable, -> { without_unapproved.without_suspended.where(moved_to_account_id: nil) }
  117. scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).joins(:account_stat) }
  118. scope :followable_by, ->(account) { joins(arel_table.join(Follow.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(Follow.arel_table[:target_account_id]).and(Follow.arel_table[:account_id].eq(account.id))).join_sources).where(Follow.arel_table[:id].eq(nil)).joins(arel_table.join(FollowRequest.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(FollowRequest.arel_table[:target_account_id]).and(FollowRequest.arel_table[:account_id].eq(account.id))).join_sources).where(FollowRequest.arel_table[:id].eq(nil)) }
  119. scope :by_recent_status, -> { order(Arel.sql('account_stats.last_status_at DESC NULLS LAST')) }
  120. scope :by_recent_sign_in, -> { order(Arel.sql('users.current_sign_in_at DESC NULLS LAST')) }
  121. scope :popular, -> { order('account_stats.followers_count desc') }
  122. scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) }
  123. scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
  124. scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
  125. after_update_commit :trigger_update_webhooks
  126. delegate :email,
  127. :unconfirmed_email,
  128. :current_sign_in_at,
  129. :created_at,
  130. :sign_up_ip,
  131. :confirmed?,
  132. :approved?,
  133. :pending?,
  134. :disabled?,
  135. :unconfirmed?,
  136. :unconfirmed_or_pending?,
  137. :role,
  138. :locale,
  139. :shows_application?,
  140. :prefers_noindex?,
  141. :time_zone,
  142. to: :user,
  143. prefix: true,
  144. allow_nil: true
  145. delegate :chosen_languages, to: :user, prefix: false, allow_nil: true
  146. update_index('accounts', :self)
  147. def local?
  148. domain.nil?
  149. end
  150. def moved?
  151. moved_to_account_id.present?
  152. end
  153. def bot?
  154. %w(Application Service).include? actor_type
  155. end
  156. def instance_actor?
  157. id == -99
  158. end
  159. alias bot bot?
  160. def bot=(val)
  161. self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person'
  162. end
  163. def group?
  164. actor_type == 'Group'
  165. end
  166. alias group group?
  167. def acct
  168. local? ? username : "#{username}@#{domain}"
  169. end
  170. def pretty_acct
  171. local? ? username : "#{username}@#{Addressable::IDNA.to_unicode(domain)}"
  172. end
  173. def local_username_and_domain
  174. "#{username}@#{Rails.configuration.x.local_domain}"
  175. end
  176. def local_followers_count
  177. Follow.where(target_account_id: id).count
  178. end
  179. def to_webfinger_s
  180. "acct:#{local_username_and_domain}"
  181. end
  182. def possibly_stale?
  183. last_webfingered_at.nil? || last_webfingered_at <= 1.day.ago
  184. end
  185. def schedule_refresh_if_stale!
  186. return unless last_webfingered_at.present? && last_webfingered_at <= BACKGROUND_REFRESH_INTERVAL.ago
  187. AccountRefreshWorker.perform_in(rand(6.hours.to_i), id)
  188. end
  189. def refresh!
  190. ResolveAccountService.new.call(acct) unless local?
  191. end
  192. def silenced?
  193. silenced_at.present?
  194. end
  195. def silence!(date = Time.now.utc)
  196. update!(silenced_at: date)
  197. end
  198. def unsilence!
  199. update!(silenced_at: nil)
  200. end
  201. def suspended?
  202. suspended_at.present? && !instance_actor?
  203. end
  204. def suspended_permanently?
  205. suspended? && deletion_request.nil?
  206. end
  207. def suspended_temporarily?
  208. suspended? && deletion_request.present?
  209. end
  210. def suspend!(date: Time.now.utc, origin: :local, block_email: true)
  211. transaction do
  212. create_deletion_request!
  213. update!(suspended_at: date, suspension_origin: origin)
  214. create_canonical_email_block! if block_email
  215. end
  216. end
  217. def unsuspend!
  218. transaction do
  219. deletion_request&.destroy!
  220. update!(suspended_at: nil, suspension_origin: nil)
  221. destroy_canonical_email_block!
  222. end
  223. end
  224. def sensitized?
  225. sensitized_at.present?
  226. end
  227. def sensitize!(date = Time.now.utc)
  228. update!(sensitized_at: date)
  229. end
  230. def unsensitize!
  231. update!(sensitized_at: nil)
  232. end
  233. def memorialize!
  234. update!(memorial: true)
  235. end
  236. def trendable?
  237. boolean_with_default('trendable', Setting.trendable_by_default)
  238. end
  239. def sign?
  240. true
  241. end
  242. def previous_strikes_count
  243. strikes.where(overruled_at: nil).count
  244. end
  245. def keypair
  246. @keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
  247. end
  248. def tags_as_strings=(tag_names)
  249. hashtags_map = Tag.find_or_create_by_names(tag_names).index_by(&:name)
  250. # Remove hashtags that are to be deleted
  251. tags.each do |tag|
  252. if hashtags_map.key?(tag.name)
  253. hashtags_map.delete(tag.name)
  254. else
  255. tags.delete(tag)
  256. end
  257. end
  258. # Add hashtags that were so far missing
  259. hashtags_map.each_value do |tag|
  260. tags << tag
  261. end
  262. end
  263. def also_known_as
  264. self[:also_known_as] || []
  265. end
  266. def fields
  267. (self[:fields] || []).filter_map do |f|
  268. Account::Field.new(self, f)
  269. rescue
  270. nil
  271. end
  272. end
  273. def fields_attributes=(attributes)
  274. fields = []
  275. old_fields = self[:fields] || []
  276. old_fields = [] if old_fields.is_a?(Hash)
  277. if attributes.is_a?(Hash)
  278. attributes.each_value do |attr|
  279. next if attr[:name].blank?
  280. previous = old_fields.find { |item| item['value'] == attr[:value] }
  281. attr[:verified_at] = previous['verified_at'] if previous && previous['verified_at'].present?
  282. fields << attr
  283. end
  284. end
  285. self[:fields] = fields
  286. end
  287. DEFAULT_FIELDS_SIZE = 4
  288. def build_fields
  289. return if fields.size >= DEFAULT_FIELDS_SIZE
  290. tmp = self[:fields] || []
  291. tmp = [] if tmp.is_a?(Hash)
  292. (DEFAULT_FIELDS_SIZE - tmp.size).times do
  293. tmp << { name: '', value: '' }
  294. end
  295. self.fields = tmp
  296. end
  297. def save_with_optional_media!
  298. save!
  299. rescue ActiveRecord::RecordInvalid => e
  300. errors = e.record.errors.errors
  301. errors.each do |err|
  302. if err.attribute == :avatar
  303. self.avatar = nil
  304. elsif err.attribute == :header
  305. self.header = nil
  306. end
  307. end
  308. save!
  309. end
  310. def hides_followers?
  311. hide_collections?
  312. end
  313. def hides_following?
  314. hide_collections?
  315. end
  316. def object_type
  317. :person
  318. end
  319. def to_param
  320. username
  321. end
  322. def to_log_human_identifier
  323. acct
  324. end
  325. def excluded_from_timeline_account_ids
  326. Rails.cache.fetch("exclude_account_ids_for:#{id}") { block_relationships.pluck(:target_account_id) + blocked_by_relationships.pluck(:account_id) + mute_relationships.pluck(:target_account_id) }
  327. end
  328. def excluded_from_timeline_domains
  329. Rails.cache.fetch("exclude_domains_for:#{id}") { domain_blocks.pluck(:domain) }
  330. end
  331. def preferred_inbox_url
  332. shared_inbox_url.presence || inbox_url
  333. end
  334. def synchronization_uri_prefix
  335. return 'local' if local?
  336. @synchronization_uri_prefix ||= "#{uri[URL_PREFIX_RE]}/"
  337. end
  338. def requires_review?
  339. reviewed_at.nil?
  340. end
  341. def reviewed?
  342. reviewed_at.present?
  343. end
  344. def requested_review?
  345. requested_review_at.present?
  346. end
  347. def requires_review_notification?
  348. requires_review? && !requested_review?
  349. end
  350. class << self
  351. def readonly_attributes
  352. super - %w(statuses_count following_count followers_count)
  353. end
  354. def inboxes
  355. urls = reorder(nil).where(protocol: :activitypub).group(:preferred_inbox_url).pluck(Arel.sql("coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url) AS preferred_inbox_url"))
  356. DeliveryFailureTracker.without_unavailable(urls)
  357. end
  358. def from_text(text)
  359. return [] if text.blank?
  360. text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.filter_map do |(username, domain)|
  361. domain = if TagManager.instance.local_domain?(domain)
  362. nil
  363. else
  364. TagManager.instance.normalize_domain(domain)
  365. end
  366. EntityCache.instance.mention(username, domain)
  367. end
  368. end
  369. def inverse_alias(key, original_key)
  370. define_method("#{key}=") do |value|
  371. public_send("#{original_key}=", !ActiveModel::Type::Boolean.new.cast(value))
  372. end
  373. define_method(key) do
  374. !public_send(original_key)
  375. end
  376. end
  377. end
  378. inverse_alias :show_collections, :hide_collections
  379. inverse_alias :unlocked, :locked
  380. def emojis
  381. @emojis ||= CustomEmoji.from_text(emojifiable_text, domain)
  382. end
  383. before_validation :prepare_contents, if: :local?
  384. before_validation :prepare_username, on: :create
  385. before_create :generate_keys
  386. before_destroy :clean_feed_manager
  387. def ensure_keys!
  388. return unless local? && private_key.blank? && public_key.blank?
  389. generate_keys
  390. save!
  391. end
  392. private
  393. def prepare_contents
  394. display_name&.strip!
  395. note&.strip!
  396. end
  397. def prepare_username
  398. username&.squish!
  399. end
  400. def generate_keys
  401. return unless local? && private_key.blank? && public_key.blank?
  402. keypair = OpenSSL::PKey::RSA.new(2048)
  403. self.private_key = keypair.to_pem
  404. self.public_key = keypair.public_key.to_pem
  405. end
  406. def normalize_domain
  407. return if local?
  408. super
  409. end
  410. def emojifiable_text
  411. [note, display_name, fields.map(&:name), fields.map(&:value)].join(' ')
  412. end
  413. def clean_feed_manager
  414. FeedManager.instance.clean_feeds!(:home, [id])
  415. end
  416. def create_canonical_email_block!
  417. return unless local? && user_email.present?
  418. begin
  419. CanonicalEmailBlock.create(reference_account: self, email: user_email)
  420. rescue ActiveRecord::RecordNotUnique
  421. # A canonical e-mail block may already exist for the same e-mail
  422. end
  423. end
  424. def destroy_canonical_email_block!
  425. return unless local?
  426. CanonicalEmailBlock.where(reference_account: self).delete_all
  427. end
  428. # NOTE: the `account.created` webhook is triggered by the `User` model, not `Account`.
  429. def trigger_update_webhooks
  430. TriggerWebhookWorker.perform_async('account.updated', 'Account', id) if local?
  431. end
  432. end