Merge branch 'main' into glitch-soc/merge-upstream
This commit is contained in:
		
						commit
						d4f315f304
					
				
					 23 changed files with 268 additions and 15 deletions
				
			
		|  | @ -117,6 +117,16 @@ module Admin | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     def unblock_email | ||||||
|  |       authorize @account, :unblock_email? | ||||||
|  | 
 | ||||||
|  |       CanonicalEmailBlock.where(reference_account: @account).delete_all | ||||||
|  | 
 | ||||||
|  |       log_action :unblock_email, @account | ||||||
|  | 
 | ||||||
|  |       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unblocked_email_msg', username: @account.acct) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     private |     private | ||||||
| 
 | 
 | ||||||
|     def set_account |     def set_account | ||||||
|  |  | ||||||
|  | @ -14,6 +14,15 @@ module Admin | ||||||
|       authorize :instance, :show? |       authorize :instance, :show? | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     def destroy | ||||||
|  |       authorize :instance, :destroy? | ||||||
|  | 
 | ||||||
|  |       Admin::DomainPurgeWorker.perform_async(@instance.domain) | ||||||
|  | 
 | ||||||
|  |       log_action :destroy, @instance | ||||||
|  |       redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     def clear_delivery_errors |     def clear_delivery_errors | ||||||
|       authorize :delivery, :clear_delivery_errors? |       authorize :delivery, :clear_delivery_errors? | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -31,6 +31,8 @@ module Admin::ActionLogsHelper | ||||||
|       link_to truncate(record.text), edit_admin_announcement_path(record.id) |       link_to truncate(record.text), edit_admin_announcement_path(record.id) | ||||||
|     when 'IpBlock' |     when 'IpBlock' | ||||||
|       "#{record.ip}/#{record.ip.prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{record.severity}")})" |       "#{record.ip}/#{record.ip.prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{record.severity}")})" | ||||||
|  |     when 'Instance' | ||||||
|  |       record.domain | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | @ -54,6 +56,8 @@ module Admin::ActionLogsHelper | ||||||
|       truncate(attributes['text'].is_a?(Array) ? attributes['text'].last : attributes['text']) |       truncate(attributes['text'].is_a?(Array) ? attributes['text'].last : attributes['text']) | ||||||
|     when 'IpBlock' |     when 'IpBlock' | ||||||
|       "#{attributes['ip']}/#{attributes['ip'].prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{attributes['severity']}")})" |       "#{attributes['ip']}/#{attributes['ip'].prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{attributes['severity']}")})" | ||||||
|  |     when 'Instance' | ||||||
|  |       attributes['domain'] | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ export default class Retention extends React.PureComponent { | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { loading, data } = this.state; |     const { loading, data } = this.state; | ||||||
|  |     const { frequency } = this.props; | ||||||
| 
 | 
 | ||||||
|     let content; |     let content; | ||||||
| 
 | 
 | ||||||
|  | @ -129,9 +130,18 @@ export default class Retention extends React.PureComponent { | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     let title = null; | ||||||
|  |     switch(frequency) { | ||||||
|  |     case 'day': | ||||||
|  |       title = <FormattedMessage id='admin.dashboard.daily_retention' defaultMessage='User retention rate by day after sign-up' />; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='retention'> |       <div className='retention'> | ||||||
|         <h4><FormattedMessage id='admin.dashboard.retention' defaultMessage='Retention' /></h4> |         <h4>{title}</h4> | ||||||
| 
 | 
 | ||||||
|         {content} |         {content} | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|  | @ -3074,17 +3074,20 @@ a.account__display-name { | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   color: $inverted-text-color; |   color: $darker-text-color; | ||||||
|   background: $simple-background-color; |   background: transparent; | ||||||
|   padding: 10px; |   padding: 7px 0; | ||||||
|   font-family: inherit; |   font-family: inherit; | ||||||
|   font-size: 14px; |   font-size: 14px; | ||||||
|   resize: vertical; |   resize: vertical; | ||||||
|   border: 0; |   border: 0; | ||||||
|  |   border-bottom: 2px solid $ui-primary-color; | ||||||
|   outline: 0; |   outline: 0; | ||||||
|   border-radius: 4px; |  | ||||||
| 
 | 
 | ||||||
|   &:focus { |   &:focus, | ||||||
|  |   &:active { | ||||||
|  |     color: $primary-text-color; | ||||||
|  |     border-bottom-color: $ui-highlight-color; | ||||||
|     outline: 0; |     outline: 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ class Admin::ActionLogFilter | ||||||
|     destroy_domain_allow: { target_type: 'DomainAllow', action: 'destroy' }.freeze, |     destroy_domain_allow: { target_type: 'DomainAllow', action: 'destroy' }.freeze, | ||||||
|     destroy_domain_block: { target_type: 'DomainBlock', action: 'destroy' }.freeze, |     destroy_domain_block: { target_type: 'DomainBlock', action: 'destroy' }.freeze, | ||||||
|     destroy_email_domain_block: { target_type: 'EmailDomainBlock', action: 'destroy' }.freeze, |     destroy_email_domain_block: { target_type: 'EmailDomainBlock', action: 'destroy' }.freeze, | ||||||
|  |     destroy_instance: { target_type: 'Instance', action: 'destroy' }.freeze, | ||||||
|     destroy_unavailable_domain: { target_type: 'UnavailableDomain', action: 'destroy' }.freeze, |     destroy_unavailable_domain: { target_type: 'UnavailableDomain', action: 'destroy' }.freeze, | ||||||
|     destroy_status: { target_type: 'Status', action: 'destroy' }.freeze, |     destroy_status: { target_type: 'Status', action: 'destroy' }.freeze, | ||||||
|     disable_2fa_user: { target_type: 'User', action: 'disable' }.freeze, |     disable_2fa_user: { target_type: 'User', action: 'disable' }.freeze, | ||||||
|  | @ -49,6 +50,7 @@ class Admin::ActionLogFilter | ||||||
|     update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, |     update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, | ||||||
|     update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze, |     update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze, | ||||||
|     update_status: { target_type: 'Status', action: 'update' }.freeze, |     update_status: { target_type: 'Status', action: 'update' }.freeze, | ||||||
|  |     unblock_email_account: { target_type: 'Account', action: 'unblock_email' }.freeze, | ||||||
|   }.freeze |   }.freeze | ||||||
| 
 | 
 | ||||||
|   attr_reader :params |   attr_reader :params | ||||||
|  |  | ||||||
|  | @ -24,4 +24,8 @@ class CanonicalEmailBlock < ApplicationRecord | ||||||
|   def self.block?(email) |   def self.block?(email) | ||||||
|     where(canonical_email_hash: email_to_canonical_email_hash(email)).exists? |     where(canonical_email_hash: email_to_canonical_email_hash(email)).exists? | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def self.find_blocks(email) | ||||||
|  |     where(canonical_email_hash: email_to_canonical_email_hash(email)) | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -64,4 +64,8 @@ class AccountPolicy < ApplicationPolicy | ||||||
|   def memorialize? |   def memorialize? | ||||||
|     admin? && !record.user&.admin? && !record.instance_actor? |     admin? && !record.user&.admin? && !record.instance_actor? | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def unblock_email? | ||||||
|  |     staff? | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -8,4 +8,8 @@ class InstancePolicy < ApplicationPolicy | ||||||
|   def show? |   def show? | ||||||
|     admin? |     admin? | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def destroy? | ||||||
|  |     admin? | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								app/services/purge_domain_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/services/purge_domain_service.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class PurgeDomainService < BaseService | ||||||
|  |   def call(domain) | ||||||
|  |     Account.remote.where(domain: domain).reorder(nil).find_each do |account| | ||||||
|  |       DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) | ||||||
|  |     end | ||||||
|  |     Instance.refresh | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -71,7 +71,9 @@ | ||||||
|           = t('admin.accounts.no_limits_imposed') |           = t('admin.accounts.no_limits_imposed') | ||||||
|       .dashboard__counters__label= t 'admin.accounts.login_status' |       .dashboard__counters__label= t 'admin.accounts.login_status' | ||||||
| 
 | 
 | ||||||
| - unless @account.local? && @account.user.nil? | - if @account.local? && @account.user.nil? | ||||||
|  |   = link_to t('admin.accounts.unblock_email'), unblock_email_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unblock_email, @account) && CanonicalEmailBlock.where(reference_account_id: @account.id).exists? | ||||||
|  | - else | ||||||
|   .table-wrapper |   .table-wrapper | ||||||
|     %table.table.inline-table |     %table.table.inline-table | ||||||
|       %tbody |       %tbody | ||||||
|  |  | ||||||
|  | @ -84,3 +84,5 @@ | ||||||
|       = link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' |       = link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' | ||||||
|     - else |     - else | ||||||
|       = link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' |       = link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' | ||||||
|  |     - unless @instance.delivery_failure_tracker.available? && @instance.accounts_count > 0 | ||||||
|  |       = link_to t('admin.instances.purge'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button' | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								app/workers/admin/domain_purge_worker.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/workers/admin/domain_purge_worker.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class Admin::DomainPurgeWorker | ||||||
|  |   include Sidekiq::Worker | ||||||
|  | 
 | ||||||
|  |   def perform(domain) | ||||||
|  |     PurgeDomainService.new.call(domain) | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -208,6 +208,8 @@ en: | ||||||
|       suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had. |       suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had. | ||||||
|       suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below. |       suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below. | ||||||
|       title: Accounts |       title: Accounts | ||||||
|  |       unblock_email: Unblock email address | ||||||
|  |       unblocked_email_msg: Successfully unblocked %{username}'s email address | ||||||
|       unconfirmed_email: Unconfirmed email |       unconfirmed_email: Unconfirmed email | ||||||
|       undo_sensitized: Undo force-sensitive |       undo_sensitized: Undo force-sensitive | ||||||
|       undo_silenced: Undo limit |       undo_silenced: Undo limit | ||||||
|  | @ -240,6 +242,7 @@ en: | ||||||
|         destroy_domain_allow: Delete Domain Allow |         destroy_domain_allow: Delete Domain Allow | ||||||
|         destroy_domain_block: Delete Domain Block |         destroy_domain_block: Delete Domain Block | ||||||
|         destroy_email_domain_block: Delete E-mail Domain Block |         destroy_email_domain_block: Delete E-mail Domain Block | ||||||
|  |         destroy_instance: Purge Domain | ||||||
|         destroy_ip_block: Delete IP rule |         destroy_ip_block: Delete IP rule | ||||||
|         destroy_status: Delete Post |         destroy_status: Delete Post | ||||||
|         destroy_unavailable_domain: Delete Unavailable Domain |         destroy_unavailable_domain: Delete Unavailable Domain | ||||||
|  | @ -261,6 +264,7 @@ en: | ||||||
|         silence_account: Limit Account |         silence_account: Limit Account | ||||||
|         suspend_account: Suspend Account |         suspend_account: Suspend Account | ||||||
|         unassigned_report: Unassign Report |         unassigned_report: Unassign Report | ||||||
|  |         unblock_email_account: Unblock email address | ||||||
|         unsensitive_account: Undo Force-Sensitive Account |         unsensitive_account: Undo Force-Sensitive Account | ||||||
|         unsilence_account: Undo Limit Account |         unsilence_account: Undo Limit Account | ||||||
|         unsuspend_account: Unsuspend Account |         unsuspend_account: Unsuspend Account | ||||||
|  | @ -287,6 +291,7 @@ en: | ||||||
|         destroy_domain_allow_html: "%{name} disallowed federation with domain %{target}" |         destroy_domain_allow_html: "%{name} disallowed federation with domain %{target}" | ||||||
|         destroy_domain_block_html: "%{name} unblocked domain %{target}" |         destroy_domain_block_html: "%{name} unblocked domain %{target}" | ||||||
|         destroy_email_domain_block_html: "%{name} unblocked e-mail domain %{target}" |         destroy_email_domain_block_html: "%{name} unblocked e-mail domain %{target}" | ||||||
|  |         destroy_instance_html: "%{name} purged domain %{target}" | ||||||
|         destroy_ip_block_html: "%{name} deleted rule for IP %{target}" |         destroy_ip_block_html: "%{name} deleted rule for IP %{target}" | ||||||
|         destroy_status_html: "%{name} removed post by %{target}" |         destroy_status_html: "%{name} removed post by %{target}" | ||||||
|         destroy_unavailable_domain_html: "%{name} resumed delivery to domain %{target}" |         destroy_unavailable_domain_html: "%{name} resumed delivery to domain %{target}" | ||||||
|  | @ -308,6 +313,7 @@ en: | ||||||
|         silence_account_html: "%{name} limited %{target}'s account" |         silence_account_html: "%{name} limited %{target}'s account" | ||||||
|         suspend_account_html: "%{name} suspended %{target}'s account" |         suspend_account_html: "%{name} suspended %{target}'s account" | ||||||
|         unassigned_report_html: "%{name} unassigned report %{target}" |         unassigned_report_html: "%{name} unassigned report %{target}" | ||||||
|  |         unblock_email_account_html: "%{name} unblocked %{target}'s email address" | ||||||
|         unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive" |         unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive" | ||||||
|         unsilence_account_html: "%{name} undid limit of %{target}'s account" |         unsilence_account_html: "%{name} undid limit of %{target}'s account" | ||||||
|         unsuspend_account_html: "%{name} unsuspended %{target}'s account" |         unsuspend_account_html: "%{name} unsuspended %{target}'s account" | ||||||
|  | @ -465,6 +471,7 @@ en: | ||||||
|       back_to_limited: Limited |       back_to_limited: Limited | ||||||
|       back_to_warning: Warning |       back_to_warning: Warning | ||||||
|       by_domain: Domain |       by_domain: Domain | ||||||
|  |       confirm_purge: Are you sure you want to permanently delete data from this domain? | ||||||
|       delivery: |       delivery: | ||||||
|         all: All |         all: All | ||||||
|         clear: Clear delivery errors |         clear: Clear delivery errors | ||||||
|  | @ -480,6 +487,7 @@ en: | ||||||
|       delivery_available: Delivery is available |       delivery_available: Delivery is available | ||||||
|       delivery_error_days: Delivery error days |       delivery_error_days: Delivery error days | ||||||
|       delivery_error_hint: If delivery is not possible for %{count} days, it will be automatically marked as undeliverable. |       delivery_error_hint: If delivery is not possible for %{count} days, it will be automatically marked as undeliverable. | ||||||
|  |       destroyed_msg: Data from %{domain} is now queued for imminent deletion. | ||||||
|       empty: No domains found. |       empty: No domains found. | ||||||
|       known_accounts: |       known_accounts: | ||||||
|         one: "%{count} known account" |         one: "%{count} known account" | ||||||
|  | @ -490,6 +498,7 @@ en: | ||||||
|         title: Moderation |         title: Moderation | ||||||
|       private_comment: Private comment |       private_comment: Private comment | ||||||
|       public_comment: Public comment |       public_comment: Public comment | ||||||
|  |       purge: Purge | ||||||
|       title: Federation |       title: Federation | ||||||
|       total_blocked_by_us: Blocked by us |       total_blocked_by_us: Blocked by us | ||||||
|       total_followed_by_them: Followed by them |       total_followed_by_them: Followed by them | ||||||
|  |  | ||||||
|  | @ -216,7 +216,7 @@ Rails.application.routes.draw do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     resources :instances, only: [:index, :show], constraints: { id: /[^\/]+/ } do |     resources :instances, only: [:index, :show, :destroy], constraints: { id: /[^\/]+/ } do | ||||||
|       member do |       member do | ||||||
|         post :clear_delivery_errors |         post :clear_delivery_errors | ||||||
|         post :restart_delivery |         post :restart_delivery | ||||||
|  | @ -251,6 +251,7 @@ Rails.application.routes.draw do | ||||||
|         post :memorialize |         post :memorialize | ||||||
|         post :approve |         post :approve | ||||||
|         post :reject |         post :reject | ||||||
|  |         post :unblock_email | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       collection do |       collection do | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ require_relative 'mastodon/preview_cards_cli' | ||||||
| require_relative 'mastodon/cache_cli' | require_relative 'mastodon/cache_cli' | ||||||
| require_relative 'mastodon/upgrade_cli' | require_relative 'mastodon/upgrade_cli' | ||||||
| require_relative 'mastodon/email_domain_blocks_cli' | require_relative 'mastodon/email_domain_blocks_cli' | ||||||
|  | require_relative 'mastodon/canonical_email_blocks_cli' | ||||||
| require_relative 'mastodon/ip_blocks_cli' | require_relative 'mastodon/ip_blocks_cli' | ||||||
| require_relative 'mastodon/maintenance_cli' | require_relative 'mastodon/maintenance_cli' | ||||||
| require_relative 'mastodon/version' | require_relative 'mastodon/version' | ||||||
|  | @ -62,6 +63,9 @@ module Mastodon | ||||||
|     desc 'ip_blocks SUBCOMMAND ...ARGS', 'Manage IP blocks' |     desc 'ip_blocks SUBCOMMAND ...ARGS', 'Manage IP blocks' | ||||||
|     subcommand 'ip_blocks', Mastodon::IpBlocksCLI |     subcommand 'ip_blocks', Mastodon::IpBlocksCLI | ||||||
| 
 | 
 | ||||||
|  |     desc 'canonical_email_blocks SUBCOMMAND ...ARGS', 'Manage canonical e-mail blocks' | ||||||
|  |     subcommand 'canonical_email_blocks', Mastodon::CanonicalEmailBlocksCLI | ||||||
|  | 
 | ||||||
|     desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities' |     desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities' | ||||||
|     subcommand 'maintenance', Mastodon::MaintenanceCLI |     subcommand 'maintenance', Mastodon::MaintenanceCLI | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										64
									
								
								lib/mastodon/canonical_email_blocks_cli.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								lib/mastodon/canonical_email_blocks_cli.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'concurrent' | ||||||
|  | require_relative '../../config/boot' | ||||||
|  | require_relative '../../config/environment' | ||||||
|  | require_relative 'cli_helper' | ||||||
|  | 
 | ||||||
|  | module Mastodon | ||||||
|  |   class CanonicalEmailBlocksCLI < Thor | ||||||
|  |     include CLIHelper | ||||||
|  | 
 | ||||||
|  |     def self.exit_on_failure? | ||||||
|  |       true | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     desc 'find EMAIL', 'Find a given e-mail address in the canonical e-mail blocks' | ||||||
|  |     long_desc <<-LONG_DESC | ||||||
|  |       When suspending a local user, a hash of a "canonical" version of their e-mail | ||||||
|  |       address is stored to prevent them from signing up again. | ||||||
|  | 
 | ||||||
|  |       This command can be used to find whether a known email address is blocked, | ||||||
|  |       and if so, which account it was attached to. | ||||||
|  |     LONG_DESC | ||||||
|  |     def find(email) | ||||||
|  |       accts = CanonicalEmailBlock.find_blocks(email).map(&:reference_account).map(&:acct).to_a | ||||||
|  |       if accts.empty? | ||||||
|  |         say("#{email} is not blocked", :yellow) | ||||||
|  |       else | ||||||
|  |         accts.each do |acct| | ||||||
|  |           say(acct, :white) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     desc 'remove EMAIL', 'Remove a canonical e-mail block' | ||||||
|  |     long_desc <<-LONG_DESC | ||||||
|  |       When suspending a local user, a hash of a "canonical" version of their e-mail | ||||||
|  |       address is stored to prevent them from signing up again. | ||||||
|  | 
 | ||||||
|  |       This command allows removing a canonical email block. | ||||||
|  |     LONG_DESC | ||||||
|  |     def remove(email) | ||||||
|  |       blocks = CanonicalEmailBlock.find_blocks(email) | ||||||
|  |       if blocks.empty? | ||||||
|  |         say("#{email} is not blocked", :yellow) | ||||||
|  |       else | ||||||
|  |         blocks.destroy_all | ||||||
|  |         say("Removed canonical email block for #{email}", :green) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     private | ||||||
|  | 
 | ||||||
|  |     def color(processed, failed) | ||||||
|  |       if !processed.zero? && failed.zero? | ||||||
|  |         :green | ||||||
|  |       elsif failed.zero? | ||||||
|  |         :yellow | ||||||
|  |       else | ||||||
|  |         :red | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -192,4 +192,36 @@ RSpec.describe Admin::AccountsController, type: :controller do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   describe 'POST #unblock_email' do | ||||||
|  |     subject do | ||||||
|  |       -> { post :unblock_email, params: { id: account.id } } | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     let(:current_user) { Fabricate(:user, admin: admin) } | ||||||
|  |     let(:account) { Fabricate(:account, suspended: true) } | ||||||
|  |     let!(:email_block) { Fabricate(:canonical_email_block, reference_account: account) } | ||||||
|  | 
 | ||||||
|  |     context 'when user is admin' do | ||||||
|  |       let(:admin) { true } | ||||||
|  | 
 | ||||||
|  |       it 'succeeds in removing email blocks' do | ||||||
|  |         is_expected.to change { CanonicalEmailBlock.where(reference_account: account).count }.from(1).to(0) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'redirects to admin account path' do | ||||||
|  |         subject.call | ||||||
|  |         expect(response).to redirect_to admin_account_path(account.id) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when user is not admin' do | ||||||
|  |       let(:admin) { false } | ||||||
|  | 
 | ||||||
|  |       it 'fails to remove avatar' do | ||||||
|  |         subject.call | ||||||
|  |         expect(response).to have_http_status :forbidden | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -3,8 +3,14 @@ require 'rails_helper' | ||||||
| RSpec.describe Admin::InstancesController, type: :controller do | RSpec.describe Admin::InstancesController, type: :controller do | ||||||
|   render_views |   render_views | ||||||
| 
 | 
 | ||||||
|  |   let(:current_user) { Fabricate(:user, admin: true) } | ||||||
|  | 
 | ||||||
|  |   let!(:account)     { Fabricate(:account, domain: 'popular') } | ||||||
|  |   let!(:account2)    { Fabricate(:account, domain: 'popular') } | ||||||
|  |   let!(:account3)    { Fabricate(:account, domain: 'less.popular') } | ||||||
|  | 
 | ||||||
|   before do |   before do | ||||||
|     sign_in Fabricate(:user, admin: true), scope: :user |     sign_in current_user, scope: :user | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'GET #index' do |   describe 'GET #index' do | ||||||
|  | @ -16,10 +22,6 @@ RSpec.describe Admin::InstancesController, type: :controller do | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'renders instances' do |     it 'renders instances' do | ||||||
|       Fabricate(:account, domain: 'popular') |  | ||||||
|       Fabricate(:account, domain: 'popular') |  | ||||||
|       Fabricate(:account, domain: 'less.popular') |  | ||||||
| 
 |  | ||||||
|       get :index, params: { page: 2 } |       get :index, params: { page: 2 } | ||||||
| 
 | 
 | ||||||
|       instances = assigns(:instances).to_a |       instances = assigns(:instances).to_a | ||||||
|  | @ -29,4 +31,27 @@ RSpec.describe Admin::InstancesController, type: :controller do | ||||||
|       expect(response).to have_http_status(200) |       expect(response).to have_http_status(200) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   describe 'DELETE #destroy' do | ||||||
|  |     subject { delete :destroy, params: { id: Instance.first.id } } | ||||||
|  | 
 | ||||||
|  |     let(:current_user) { Fabricate(:user, admin: admin) } | ||||||
|  |     let(:account) { Fabricate(:account) } | ||||||
|  | 
 | ||||||
|  |     context 'when user is admin' do | ||||||
|  |       let(:admin) { true } | ||||||
|  | 
 | ||||||
|  |       it 'succeeds in purging instance' do | ||||||
|  |         is_expected.to redirect_to admin_instances_path | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when user is not admin' do | ||||||
|  |       let(:admin) { false } | ||||||
|  | 
 | ||||||
|  |       it 'fails to purge instance' do | ||||||
|  |         is_expected.to have_http_status :forbidden | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ RSpec.describe AccountPolicy do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   permissions :unsuspend? do |   permissions :unsuspend?, :unblock_email? do | ||||||
|     before do |     before do | ||||||
|       alice.suspend! |       alice.suspend! | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ RSpec.describe InstancePolicy do | ||||||
|   let(:admin)   { Fabricate(:user, admin: true).account } |   let(:admin)   { Fabricate(:user, admin: true).account } | ||||||
|   let(:john)    { Fabricate(:user).account } |   let(:john)    { Fabricate(:user).account } | ||||||
| 
 | 
 | ||||||
|   permissions :index? do |   permissions :index?, :show?, :destroy? do | ||||||
|     context 'admin' do |     context 'admin' do | ||||||
|       it 'permits' do |       it 'permits' do | ||||||
|         expect(subject).to permit(admin, Instance) |         expect(subject).to permit(admin, Instance) | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								spec/services/purge_domain_service_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								spec/services/purge_domain_service_spec.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe PurgeDomainService, type: :service do | ||||||
|  |   let!(:old_account) { Fabricate(:account, domain: 'obsolete.org') } | ||||||
|  |   let!(:old_status1) { Fabricate(:status, account: old_account) } | ||||||
|  |   let!(:old_status2) { Fabricate(:status, account: old_account) } | ||||||
|  |   let!(:old_attachment) { Fabricate(:media_attachment, account: old_account, status: old_status2, file: attachment_fixture('attachment.jpg')) } | ||||||
|  | 
 | ||||||
|  |   subject { PurgeDomainService.new } | ||||||
|  | 
 | ||||||
|  |   describe 'for a suspension' do | ||||||
|  |     before do | ||||||
|  |       subject.call('obsolete.org') | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'removes the remote accounts\'s statuses and media attachments' do | ||||||
|  |       expect { old_account.reload }.to raise_exception ActiveRecord::RecordNotFound | ||||||
|  |       expect { old_status1.reload }.to raise_exception ActiveRecord::RecordNotFound | ||||||
|  |       expect { old_status2.reload }.to raise_exception ActiveRecord::RecordNotFound | ||||||
|  |       expect { old_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'refreshes instances view' do | ||||||
|  |       expect(Instance.where(domain: 'obsolete.org').exists?).to be false | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										18
									
								
								spec/workers/admin/domain_purge_worker_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								spec/workers/admin/domain_purge_worker_spec.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | describe Admin::DomainPurgeWorker do | ||||||
|  |   subject { described_class.new } | ||||||
|  | 
 | ||||||
|  |   describe 'perform' do | ||||||
|  |     it 'calls domain purge service for relevant domain block' do | ||||||
|  |       service = double(call: nil) | ||||||
|  |       allow(PurgeDomainService).to receive(:new).and_return(service) | ||||||
|  |       result = subject.perform('example.com') | ||||||
|  | 
 | ||||||
|  |       expect(result).to be_nil | ||||||
|  |       expect(service).to have_received(:call).with('example.com') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
		Loading…
	
		Reference in a new issue