Browse Source

Record account suspend/silence time and keep track of domain blocks (#10660)

* Record account suspend/silence time and keep track of domain blocks

* Also unblock users who were suspended/silenced before dates were recorded

* Add tests

* Keep track of suspending date for users suspended through the CLI

* Show accurate number of accounts that would be affected by unsuspending an instance

* Change migration to set silenced_at and suspended_at

* Revert "Also unblock users who were suspended/silenced before dates were recorded"

This reverts commit a015c65d2d.

* Switch from using suspended and silenced to suspended_at and silenced_at

* Add post-deployment migration script to remove `suspended` and `silenced` columns

* Use Account#silence! and Account#suspend! instead of updating the underlying property

* Add silenced_at and suspended_at migration to post-migration

* Change account fabricator to translate suspended and silenced attributes

* Minor fixes

* Make unblocking domains always retroactive
pull/3/head
ThibG 2 months ago
parent
commit
14f6ce2885

+ 2
- 6
app/controllers/admin/domain_blocks_controller.rb View File

@@ -41,7 +41,7 @@ module Admin
41 41
 
42 42
     def destroy
43 43
       authorize @domain_block, :destroy?
44
-      UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
44
+      UnblockDomainService.new.call(@domain_block)
45 45
       log_action :destroy, @domain_block
46 46
       redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.destroyed_msg')
47 47
     end
@@ -53,11 +53,7 @@ module Admin
53 53
     end
54 54
 
55 55
     def resource_params
56
-      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
57
-    end
58
-
59
-    def retroactive_unblock?
60
-      ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive])
56
+      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports)
61 57
     end
62 58
   end
63 59
 end

+ 1
- 1
app/controllers/home_controller.rb View File

@@ -58,7 +58,7 @@ class HomeController < ApplicationController
58 58
     if request.path.start_with?('/web')
59 59
       new_user_session_path
60 60
     elsif single_user_mode?
61
-      short_account_path(Account.local.where(suspended: false).first)
61
+      short_account_path(Account.local.without_suspended.first)
62 62
     else
63 63
       about_path
64 64
     end

+ 25
- 15
app/models/account.rb View File

@@ -28,8 +28,6 @@
28 28
 #  header_updated_at       :datetime
29 29
 #  avatar_remote_url       :string
30 30
 #  subscription_expires_at :datetime
31
-#  silenced                :boolean          default(FALSE), not null
32
-#  suspended               :boolean          default(FALSE), not null
33 31
 #  locked                  :boolean          default(FALSE), not null
34 32
 #  header_remote_url       :string           default(""), not null
35 33
 #  last_webfingered_at     :datetime
@@ -45,6 +43,8 @@
45 43
 #  actor_type              :string
46 44
 #  discoverable            :boolean
47 45
 #  also_known_as           :string           is an Array
46
+#  silenced_at             :datetime
47
+#  suspended_at            :datetime
48 48
 #
49 49
 
50 50
 class Account < ApplicationRecord
@@ -82,10 +82,10 @@ class Account < ApplicationRecord
82 82
   scope :local, -> { where(domain: nil) }
83 83
   scope :expiring, ->(time) { remote.where.not(subscription_expires_at: nil).where('subscription_expires_at < ?', time) }
84 84
   scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
85
-  scope :silenced, -> { where(silenced: true) }
86
-  scope :suspended, -> { where(suspended: true) }
87
-  scope :without_suspended, -> { where(suspended: false) }
88
-  scope :without_silenced, -> { where(silenced: false) }
85
+  scope :silenced, -> { where.not(silenced_at: nil) }
86
+  scope :suspended, -> { where.not(suspended_at: nil) }
87
+  scope :without_suspended, -> { where(suspended_at: nil) }
88
+  scope :without_silenced, -> { where(silenced_at: nil) }
89 89
   scope :recent, -> { reorder(id: :desc) }
90 90
   scope :bots, -> { where(actor_type: %w(Application Service)) }
91 91
   scope :alphabetic, -> { order(domain: :asc, username: :asc) }
@@ -165,25 +165,35 @@ class Account < ApplicationRecord
165 165
     ResolveAccountService.new.call(acct)
166 166
   end
167 167
 
168
-  def silence!
169
-    update!(silenced: true)
168
+  def silenced?
169
+    silenced_at.present?
170
+  end
171
+
172
+  def silence!(date = nil)
173
+    date ||= Time.now.utc
174
+    update!(silenced_at: date)
170 175
   end
171 176
 
172 177
   def unsilence!
173
-    update!(silenced: false)
178
+    update!(silenced_at: nil)
179
+  end
180
+
181
+  def suspended?
182
+    suspended_at.present?
174 183
   end
175 184
 
176
-  def suspend!
185
+  def suspend!(date = nil)
186
+    date ||= Time.now.utc
177 187
     transaction do
178 188
       user&.disable! if local?
179
-      update!(suspended: true)
189
+      update!(suspended_at: date)
180 190
     end
181 191
   end
182 192
 
183 193
   def unsuspend!
184 194
     transaction do
185 195
       user&.enable! if local?
186
-      update!(suspended: false)
196
+      update!(suspended_at: nil)
187 197
     end
188 198
   end
189 199
 
@@ -399,7 +409,7 @@ class Account < ApplicationRecord
399 409
           ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
400 410
         FROM accounts
401 411
         WHERE #{query} @@ #{textsearch}
402
-          AND accounts.suspended = false
412
+          AND accounts.suspended_at IS NULL
403 413
           AND accounts.moved_to_account_id IS NULL
404 414
         ORDER BY rank DESC
405 415
         LIMIT ? OFFSET ?
@@ -427,7 +437,7 @@ class Account < ApplicationRecord
427 437
           LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
428 438
           WHERE accounts.id IN (SELECT * FROM first_degree)
429 439
             AND #{query} @@ #{textsearch}
430
-            AND accounts.suspended = false
440
+            AND accounts.suspended_at IS NULL
431 441
             AND accounts.moved_to_account_id IS NULL
432 442
           GROUP BY accounts.id
433 443
           ORDER BY rank DESC
@@ -443,7 +453,7 @@ class Account < ApplicationRecord
443 453
           FROM accounts
444 454
           LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
445 455
           WHERE #{query} @@ #{textsearch}
446
-            AND accounts.suspended = false
456
+            AND accounts.suspended_at IS NULL
447 457
             AND accounts.moved_to_account_id IS NULL
448 458
           GROUP BY accounts.id
449 459
           ORDER BY rank DESC

+ 1
- 1
app/models/concerns/account_finder_concern.rb View File

@@ -13,7 +13,7 @@ module AccountFinderConcern
13 13
     end
14 14
 
15 15
     def representative
16
-      find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) || Account.local.find_by(suspended: false)
16
+      find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) || Account.local.without_suspended.first
17 17
     end
18 18
 
19 19
     def find_local(username)

+ 5
- 2
app/models/domain_block.rb View File

@@ -17,8 +17,6 @@ class DomainBlock < ApplicationRecord
17 17
 
18 18
   enum severity: [:silence, :suspend, :noop]
19 19
 
20
-  attr_accessor :retroactive
21
-
22 20
   validates :domain, presence: true, uniqueness: true
23 21
 
24 22
   has_many :accounts, foreign_key: :domain, primary_key: :domain
@@ -36,4 +34,9 @@ class DomainBlock < ApplicationRecord
36 34
     return false if other_block.silence? && noop?
37 35
     (reject_media || !other_block.reject_media) && (reject_reports || !other_block.reject_reports)
38 36
   end
37
+
38
+  def affected_accounts_count
39
+    scope = suspend? ? accounts.where(suspended_at: created_at) : accounts.where(silenced_at: created_at)
40
+    scope.count
41
+  end
39 42
 end

+ 2
- 2
app/models/status.rb View File

@@ -84,8 +84,8 @@ class Status < ApplicationRecord
84 84
   scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
85 85
   scope :with_public_visibility, -> { where(visibility: :public) }
86 86
   scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
87
-  scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: false }) }
88
-  scope :including_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: true }) }
87
+  scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced_at: nil }) }
88
+  scope :including_silenced_accounts, -> { left_outer_joins(:account).where.not(accounts: { silenced_at: nil }) }
89 89
   scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
90 90
   scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) }
91 91
   scope :tagged_with_all, ->(tags) {

+ 1
- 1
app/models/user.rb View File

@@ -88,7 +88,7 @@ class User < ApplicationRecord
88 88
   scope :confirmed, -> { where.not(confirmed_at: nil) }
89 89
   scope :enabled, -> { where(disabled: false) }
90 90
   scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
91
-  scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where(accounts: { suspended: false }) }
91
+  scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where.not(accounts: { suspended_at: nil }) }
92 92
   scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
93 93
   scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
94 94
 

+ 6
- 6
app/services/activitypub/process_account_service.rb View File

@@ -50,12 +50,12 @@ class ActivityPub::ProcessAccountService < BaseService
50 50
 
51 51
   def create_account
52 52
     @account = Account.new
53
-    @account.protocol    = :activitypub
54
-    @account.username    = @username
55
-    @account.domain      = @domain
56
-    @account.suspended   = true if auto_suspend?
57
-    @account.silenced    = true if auto_silence?
58
-    @account.private_key = nil
53
+    @account.protocol     = :activitypub
54
+    @account.username     = @username
55
+    @account.domain       = @domain
56
+    @account.private_key  = nil
57
+    @account.suspended_at = domain_block.created_at if auto_suspend?
58
+    @account.silenced_at = domain_block.created_at if auto_silence?
59 59
   end
60 60
 
61 61
   def update_account

+ 3
- 3
app/services/block_domain_service.rb View File

@@ -29,7 +29,7 @@ class BlockDomainService < BaseService
29 29
   end
30 30
 
31 31
   def silence_accounts!
32
-    blocked_domain_accounts.in_batches.update_all(silenced: true)
32
+    blocked_domain_accounts.without_silenced.in_batches.update_all(silenced_at: @domain_block.created_at)
33 33
   end
34 34
 
35 35
   def clear_media!
@@ -43,9 +43,9 @@ class BlockDomainService < BaseService
43 43
   end
44 44
 
45 45
   def suspend_accounts!
46
-    blocked_domain_accounts.where(suspended: false).reorder(nil).find_each do |account|
46
+    blocked_domain_accounts.without_suspended.reorder(nil).find_each do |account|
47 47
       UnsubscribeService.new.call(account) if account.subscribed?
48
-      SuspendAccountService.new.call(account)
48
+      SuspendAccountService.new.call(account, suspended_at: @domain_block.created_at)
49 49
     end
50 50
   end
51 51
 

+ 1
- 1
app/services/post_status_service.rb View File

@@ -49,7 +49,7 @@ class PostStatusService < BaseService
49 49
   def preprocess_attributes!
50 50
     @text         = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
51 51
     @visibility   = @options[:visibility] || @account.user&.setting_default_privacy
52
-    @visibility   = :unlisted if @visibility == :public && @account.silenced
52
+    @visibility   = :unlisted if @visibility == :public && @account.silenced?
53 53
     @scheduled_at = @options[:scheduled_at]&.to_datetime
54 54
     @scheduled_at = nil if scheduled_in_the_past?
55 55
   rescue ArgumentError

+ 1
- 1
app/services/process_mentions_service.rb View File

@@ -25,7 +25,7 @@ class ProcessMentionsService < BaseService
25 25
         end
26 26
       end
27 27
 
28
-      next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended
28
+      next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended?
29 29
 
30 30
       mentions << mentioned_account.mentions.where(status: status).first_or_create(status: status)
31 31
 

+ 3
- 3
app/services/resolve_account_service.rb View File

@@ -119,9 +119,9 @@ class ResolveAccountService < BaseService
119 119
     Rails.logger.debug "Creating new remote account for #{@username}@#{@domain}"
120 120
 
121 121
     @account = Account.new(username: @username, domain: @domain)
122
-    @account.suspended   = true if auto_suspend?
123
-    @account.silenced    = true if auto_silence?
124
-    @account.private_key = nil
122
+    @account.suspended_at = domain_block.created_at if auto_suspend?
123
+    @account.silenced_at  = domain_block.created_at if auto_silence?
124
+    @account.private_key  = nil
125 125
   end
126 126
 
127 127
   def update_account

+ 1
- 1
app/services/subscribe_service.rb View File

@@ -43,7 +43,7 @@ class SubscribeService < BaseService
43 43
   end
44 44
 
45 45
   def some_local_account
46
-    @some_local_account ||= Account.local.where(suspended: false).first
46
+    @some_local_account ||= Account.local.without_suspended.first
47 47
   end
48 48
 
49 49
   # Any response in the 3xx or 4xx range, except for 429 (rate limit)

+ 2
- 2
app/services/suspend_account_service.rb View File

@@ -88,8 +88,8 @@ class SuspendAccountService < BaseService
88 88
 
89 89
     return if @options[:destroy]
90 90
 
91
-    @account.silenced         = false
92
-    @account.suspended        = true
91
+    @account.silenced_at      = nil
92
+    @account.suspended_at     = @options[:suspended_at] || Time.now.utc
93 93
     @account.locked           = false
94 94
     @account.display_name     = ''
95 95
     @account.note             = ''

+ 10
- 5
app/services/unblock_domain_service.rb View File

@@ -3,9 +3,9 @@
3 3
 class UnblockDomainService < BaseService
4 4
   attr_accessor :domain_block
5 5
 
6
-  def call(domain_block, retroactive)
6
+  def call(domain_block)
7 7
     @domain_block = domain_block
8
-    process_retroactive_updates if retroactive
8
+    process_retroactive_updates
9 9
     domain_block.destroy
10 10
   end
11 11
 
@@ -14,14 +14,19 @@ class UnblockDomainService < BaseService
14 14
   end
15 15
 
16 16
   def blocked_accounts
17
-    Account.where(domain: domain_block.domain)
17
+    scope = Account.where(domain: domain_block.domain)
18
+    if domain_block.silence?
19
+      scope.where(silenced_at: @domain_block.created_at)
20
+    else
21
+      scope.where(suspended_at: @domain_block.created_at)
22
+    end
18 23
   end
19 24
 
20 25
   def update_options
21
-    { domain_block_impact => false }
26
+    { domain_block_impact => nil }
22 27
   end
23 28
 
24 29
   def domain_block_impact
25
-    domain_block.silence? ? :silenced : :suspended
30
+    domain_block.silence? ? :silenced_at : :suspended_at
26 31
   end
27 32
 end

+ 5
- 12
app/views/admin/domain_blocks/show.html.haml View File

@@ -3,18 +3,11 @@
3 3
 
4 4
 = simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :delete do |f|
5 5
 
6
-  - if (@domain_block.noop?)
7
-    = f.input :retroactive,
8
-      as: :hidden,
9
-      input_html: { :value => "0" }
10
-  - else
11
-    = f.input :retroactive,
12
-      as: :boolean,
13
-      wrapper: :with_label,
14
-      label: t(".retroactive.#{@domain_block.severity}"),
15
-      hint: t(:affected_accounts,
16
-        scope: [:admin, :domain_blocks, :show],
17
-        count: @domain_block.accounts_count)
6
+  - unless (@domain_block.noop?)
7
+    %p= t(".retroactive.#{@domain_block.severity}")
8
+    %p.hint= t(:affected_accounts,
9
+      scope: [:admin, :domain_blocks, :show],
10
+      count: @domain_block.affected_accounts_count)
18 11
 
19 12
   .actions
20 13
     = f.button :button, t('.undo'), type: :submit

+ 2
- 2
config/locales/en.yml View File

@@ -293,8 +293,8 @@ en:
293 293
           one: One account in the database affected
294 294
           other: "%{count} accounts in the database affected"
295 295
         retroactive:
296
-          silence: Unsilence all existing accounts from this domain
297
-          suspend: Unsuspend all existing accounts from this domain
296
+          silence: Unsilence existing affected accounts from this domain
297
+          suspend: Unsuspend existing affected accounts from this domain
298 298
         title: Undo domain block for %{domain}
299 299
         undo: Undo
300 300
       undo: Undo domain block

+ 41
- 0
db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb View File

@@ -0,0 +1,41 @@
1
+class AddSilencedAtSuspendedAtToAccounts < ActiveRecord::Migration[5.2]
2
+  class Account < ApplicationRecord
3
+    # Dummy class, to make migration possible across version changes
4
+  end
5
+
6
+  class DomainBlock < ApplicationRecord
7
+    # Dummy class, to make migration possible across version changes
8
+    enum severity: [:silence, :suspend, :noop]
9
+
10
+    has_many :accounts, foreign_key: :domain, primary_key: :domain
11
+  end
12
+
13
+  def up
14
+    add_column :accounts, :silenced_at, :datetime
15
+    add_column :accounts, :suspended_at, :datetime
16
+
17
+    # Record suspend date of blocks and silences for users whose limitations match
18
+    # a domain block
19
+    DomainBlock.where(severity: [:silence, :suspend]).find_each do |block|
20
+      scope = block.accounts
21
+      if block.suspend?
22
+        block.accounts.where(suspended: true).in_batches.update_all(suspended_at: block.created_at)
23
+      else
24
+        block.accounts.where(silenced: true).in_batches.update_all(silenced_at: block.created_at)
25
+      end
26
+    end
27
+
28
+    # Set dates for accounts which have limitations not related to a domain block
29
+    Account.where(suspended: true, suspended_at: nil).in_batches.update_all(suspended_at: Time.now.utc)
30
+    Account.where(silenced: true, silenced_at: nil).in_batches.update_all(silenced_at: Time.now.utc)
31
+  end
32
+
33
+  def down
34
+    # Block or silence accounts that have a date set
35
+    Account.where(suspended: false).where.not(suspended_at: nil).in_batches.update_all(suspended: true)
36
+    Account.where(silenced: false).where.not(silenced_at: nil).in_batches.update_all(silenced: true)
37
+
38
+    remove_column :accounts, :silenced_at
39
+    remove_column :accounts, :suspended_at
40
+  end
41
+end

+ 45
- 0
db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb View File

@@ -0,0 +1,45 @@
1
+# frozen_string_literal: true
2
+
3
+class RemoveSuspendedSilencedAccountFields < ActiveRecord::Migration[5.2]
4
+  class Account < ApplicationRecord
5
+    # Dummy class, to make migration possible across version changes
6
+  end
7
+
8
+  class DomainBlock < ApplicationRecord
9
+    # Dummy class, to make migration possible across version changes
10
+    enum severity: [:silence, :suspend, :noop]
11
+
12
+    has_many :accounts, foreign_key: :domain, primary_key: :domain
13
+  end
14
+
15
+  disable_ddl_transaction!
16
+
17
+  def up
18
+    # Record suspend date of blocks and silences for users whose limitations match
19
+    # a domain block
20
+    DomainBlock.where(severity: [:silence, :suspend]).find_each do |block|
21
+      scope = block.accounts
22
+      if block.suspend?
23
+        block.accounts.where(suspended: true).in_batches.update_all(suspended_at: block.created_at)
24
+      else
25
+        block.accounts.where(silenced: true).in_batches.update_all(silenced_at: block.created_at)
26
+      end
27
+    end
28
+
29
+    # Set dates for accounts which have limitations not related to a domain block
30
+    Account.where(suspended: true, suspended_at: nil).in_batches.update_all(suspended_at: Time.now.utc)
31
+    Account.where(silenced: true, silenced_at: nil).in_batches.update_all(silenced_at: Time.now.utc)
32
+
33
+    safety_assured do
34
+      remove_column :accounts, :suspended, :boolean, null: false, default: false
35
+      remove_column :accounts, :silenced, :boolean, null: false, default: false
36
+    end
37
+  end
38
+
39
+  def down
40
+    safety_assured do
41
+      add_column :accounts, :suspended, :boolean, null: false, default: false
42
+      add_column :accounts, :silenced, :boolean, null: false, default: false
43
+    end
44
+  end
45
+end

+ 3
- 3
db/schema.rb View File

@@ -10,7 +10,7 @@
10 10
 #
11 11
 # It's strongly recommended that you check this file into your version control system.
12 12
 
13
-ActiveRecord::Schema.define(version: 2019_05_09_164208) do
13
+ActiveRecord::Schema.define(version: 2019_05_11_152737) do
14 14
 
15 15
   # These are extensions that must be enabled in order to support this database
16 16
   enable_extension "plpgsql"
@@ -131,8 +131,6 @@ ActiveRecord::Schema.define(version: 2019_05_09_164208) do
131 131
     t.datetime "header_updated_at"
132 132
     t.string "avatar_remote_url"
133 133
     t.datetime "subscription_expires_at"
134
-    t.boolean "silenced", default: false, null: false
135
-    t.boolean "suspended", default: false, null: false
136 134
     t.boolean "locked", default: false, null: false
137 135
     t.string "header_remote_url", default: "", null: false
138 136
     t.datetime "last_webfingered_at"
@@ -148,6 +146,8 @@ ActiveRecord::Schema.define(version: 2019_05_09_164208) do
148 146
     t.string "actor_type"
149 147
     t.boolean "discoverable"
150 148
     t.string "also_known_as", array: true
149
+    t.datetime "silenced_at"
150
+    t.datetime "suspended_at"
151 151
     t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
152 152
     t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true
153 153
     t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id"

+ 1
- 1
lib/cli.rb View File

@@ -106,7 +106,7 @@ module Mastodon
106 106
             [json, account.id, inbox_url]
107 107
           end
108 108
 
109
-          account.update_column(:suspended, true)
109
+          account.suspend!
110 110
         end
111 111
 
112 112
         processed += 1

+ 2
- 2
lib/mastodon/accounts_cli.rb View File

@@ -87,8 +87,8 @@ module Mastodon
87 87
         end
88 88
       end
89 89
 
90
-      account.suspended = false
91
-      user.account      = account
90
+      account.suspended_at = nil
91
+      user.account         = account
92 92
 
93 93
       if user.save
94 94
         if options[:confirmed]

+ 2
- 2
spec/controllers/admin/domain_blocks_controller_spec.rb View File

@@ -63,9 +63,9 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do
63 63
       service = double(call: true)
64 64
       allow(UnblockDomainService).to receive(:new).and_return(service)
65 65
       domain_block = Fabricate(:domain_block)
66
-      delete :destroy, params: { id: domain_block.id, domain_block: { retroactive: '1' } }
66
+      delete :destroy, params: { id: domain_block.id }
67 67
 
68
-      expect(service).to have_received(:call).with(domain_block, true)
68
+      expect(service).to have_received(:call).with(domain_block)
69 69
       expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.destroyed_msg')
70 70
       expect(response).to redirect_to(admin_instances_path(limited: '1'))
71 71
     end

+ 3
- 0
spec/fabricators/account_fabricator.rb View File

@@ -3,8 +3,11 @@ public_key  = keypair.public_key.to_pem
3 3
 private_key = keypair.to_pem
4 4
 
5 5
 Fabricator(:account) do
6
+  transient :suspended, :silenced
6 7
   username            { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } }
7 8
   last_webfingered_at { Time.now.utc }
8 9
   public_key          { public_key }
9 10
   private_key         { private_key }
11
+  suspended_at        { |attrs| attrs[:suspended] ? Time.now.utc : nil }
12
+  silenced_at         { |attrs| attrs[:silenced] ? Time.now.utc : nil }
10 13
 end

+ 2
- 2
spec/lib/feed_manager_spec.rb View File

@@ -168,13 +168,13 @@ RSpec.describe FeedManager do
168 168
 
169 169
       it 'returns true for status by silenced account who recipient is not following' do
170 170
         status = Fabricate(:status, text: 'Hello world', account: alice)
171
-        alice.update(silenced: true)
171
+        alice.silence!
172 172
         expect(FeedManager.instance.filter?(:mentions, status, bob.id)).to be true
173 173
       end
174 174
 
175 175
       it 'returns false for status by followed silenced account' do
176 176
         status = Fabricate(:status, text: 'Hello world', account: alice)
177
-        alice.update(silenced: true)
177
+        alice.silence!
178 178
         bob.follow!(alice)
179 179
         expect(FeedManager.instance.filter?(:mentions, status, bob.id)).to be false
180 180
       end

+ 2
- 2
spec/lib/status_filter_spec.rb View File

@@ -15,7 +15,7 @@ describe StatusFilter do
15 15
 
16 16
       context 'when status account is silenced' do
17 17
         before do
18
-          status.account.update(silenced: true)
18
+          status.account.silence!
19 19
         end
20 20
 
21 21
         it { is_expected.to be_filtered }
@@ -65,7 +65,7 @@ describe StatusFilter do
65 65
 
66 66
       context 'when status account is silenced' do
67 67
         before do
68
-          status.account.update(silenced: true)
68
+          status.account.silence!
69 69
         end
70 70
 
71 71
         it { is_expected.to be_filtered }

+ 2
- 2
spec/models/concerns/status_threading_concern_spec.rb View File

@@ -35,7 +35,7 @@ describe StatusThreadingConcern do
35 35
     end
36 36
 
37 37
     it 'does not return conversation history from silenced and not followed users' do
38
-      jeff.update(silenced: true)
38
+      jeff.silence!
39 39
       expect(reply3.ancestors(4, viewer)).to_not include(reply1)
40 40
     end
41 41
 
@@ -110,7 +110,7 @@ describe StatusThreadingConcern do
110 110
     end
111 111
 
112 112
     it 'does not return replies from silenced and not followed users' do
113
-      jeff.update(silenced: true)
113
+      jeff.silence!
114 114
       expect(status.descendants(4, viewer)).to_not include(reply3)
115 115
     end
116 116
 

+ 29
- 11
spec/services/block_domain_service_spec.rb View File

@@ -1,20 +1,14 @@
1 1
 require 'rails_helper'
2 2
 
3 3
 RSpec.describe BlockDomainService, type: :service do
4
-  let(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
5
-  let(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
6
-  let(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
7
-  let(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
4
+  let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
5
+  let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
6
+  let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
7
+  let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
8
+  let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: 'evil.org', suspended: true, silenced: true) }
8 9
 
9 10
   subject { BlockDomainService.new }
10 11
 
11
-  before do
12
-    bad_account
13
-    bad_status1
14
-    bad_status2
15
-    bad_attachment
16
-  end
17
-
18 12
   describe 'for a suspension' do
19 13
     before do
20 14
       subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
@@ -28,6 +22,18 @@ RSpec.describe BlockDomainService, type: :service do
28 22
       expect(Account.find_remote('badguy666', 'evil.org').suspended?).to be true
29 23
     end
30 24
 
25
+    it 'records suspension date appropriately' do
26
+      expect(Account.find_remote('badguy666', 'evil.org').suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
27
+    end
28
+
29
+    it 'keeps already-banned accounts banned' do
30
+      expect(Account.find_remote('badguy', 'evil.org').suspended?).to be true
31
+    end
32
+
33
+    it 'does not overwrite suspension date of already-banned accounts' do
34
+      expect(Account.find_remote('badguy', 'evil.org').suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
35
+    end
36
+
31 37
     it 'removes the remote accounts\'s statuses and media attachments' do
32 38
       expect { bad_status1.reload }.to raise_exception ActiveRecord::RecordNotFound
33 39
       expect { bad_status2.reload }.to raise_exception ActiveRecord::RecordNotFound
@@ -48,6 +54,18 @@ RSpec.describe BlockDomainService, type: :service do
48 54
       expect(Account.find_remote('badguy666', 'evil.org').silenced?).to be true
49 55
     end
50 56
 
57
+    it 'records suspension date appropriately' do
58
+      expect(Account.find_remote('badguy666', 'evil.org').silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
59
+    end
60
+
61
+    it 'keeps already-banned accounts banned' do
62
+      expect(Account.find_remote('badguy', 'evil.org').silenced?).to be true
63
+    end
64
+
65
+    it 'does not overwrite suspension date of already-banned accounts' do
66
+      expect(Account.find_remote('badguy', 'evil.org').silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
67
+    end
68
+
51 69
     it 'leaves the domains status and attachements, but clears media' do
52 70
       expect { bad_status1.reload }.not_to raise_error
53 71
       expect { bad_status2.reload }.not_to raise_error

+ 2
- 2
spec/services/notify_service_spec.rb View File

@@ -39,12 +39,12 @@ RSpec.describe NotifyService, type: :service do
39 39
   end
40 40
 
41 41
   it 'does not notify when sender is silenced and not followed' do
42
-    sender.update(silenced: true)
42
+    sender.silence!
43 43
     is_expected.to_not change(Notification, :count)
44 44
   end
45 45
 
46 46
   it 'does not notify when recipient is suspended' do
47
-    recipient.update(suspended: true)
47
+    recipient.suspend!
48 48
     is_expected.to_not change(Notification, :count)
49 49
   end
50 50
 

+ 21
- 24
spec/services/unblock_domain_service_spec.rb View File

@@ -7,36 +7,33 @@ describe UnblockDomainService, type: :service do
7 7
 
8 8
   describe 'call' do
9 9
     before do
10
-      @silenced = Fabricate(:account, domain: 'example.com', silenced: true)
11
-      @suspended = Fabricate(:account, domain: 'example.com', suspended: true)
10
+      @independently_suspended = Fabricate(:account, domain: 'example.com', suspended_at: 1.hour.ago)
11
+      @independently_silenced = Fabricate(:account, domain: 'example.com', silenced_at: 1.hour.ago)
12 12
       @domain_block = Fabricate(:domain_block, domain: 'example.com')
13
+      @silenced = Fabricate(:account, domain: 'example.com', silenced_at: @domain_block.created_at)
14
+      @suspended = Fabricate(:account, domain: 'example.com', suspended_at: @domain_block.created_at)
13 15
     end
14 16
 
15
-    context 'without retroactive' do
16
-      it 'removes the domain block' do
17
-        subject.call(@domain_block, false)
18
-        expect_deleted_domain_block
19
-      end
20
-    end
21
-
22
-    context 'with retroactive' do
23
-      it 'unsilences accounts and removes block' do
24
-        @domain_block.update(severity: :silence)
17
+    it 'unsilences accounts and removes block' do
18
+      @domain_block.update(severity: :silence)
25 19
 
26
-        subject.call(@domain_block, true)
27
-        expect_deleted_domain_block
28
-        expect(@silenced.reload.silenced).to be false
29
-        expect(@suspended.reload.suspended).to be true
30
-      end
20
+      subject.call(@domain_block)
21
+      expect_deleted_domain_block
22
+      expect(@silenced.reload.silenced?).to be false
23
+      expect(@suspended.reload.suspended?).to be true
24
+      expect(@independently_suspended.reload.suspended?).to be true
25
+      expect(@independently_silenced.reload.silenced?).to be true
26
+    end
31 27
 
32
-      it 'unsuspends accounts and removes block' do
33
-        @domain_block.update(severity: :suspend)
28
+    it 'unsuspends accounts and removes block' do
29
+      @domain_block.update(severity: :suspend)
34 30
 
35
-        subject.call(@domain_block, true)
36
-        expect_deleted_domain_block
37
-        expect(@suspended.reload.suspended).to be false
38
-        expect(@silenced.reload.silenced).to be true
39
-      end
31
+      subject.call(@domain_block)
32
+      expect_deleted_domain_block
33
+      expect(@suspended.reload.suspended?).to be false
34
+      expect(@silenced.reload.silenced?).to be true
35
+      expect(@independently_suspended.reload.suspended?).to be true
36
+      expect(@independently_silenced.reload.silenced?).to be true
40 37
     end
41 38
   end
42 39
 

Loading…
Cancel
Save