1
0

account.rb 15 KB

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