account.rb 15 KB

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