From d6fe0c94ca48a10d579cf5ec321c33c6124b402d Mon Sep 17 00:00:00 2001 From: Takeshi Umeda <noel.yoshiba@gmail.com> Date: Thu, 5 Nov 2020 04:45:01 +0900 Subject: [PATCH] Add account sensitized (#14361) * Add account sensitized * Fix i18n normalize * Fix description and spec * Fix spec * Fix wording --- app/controllers/admin/accounts_controller.rb | 7 +++++++ .../api/v1/admin/accounts_controller.rb | 8 ++++++++ app/helpers/statuses_helper.rb | 8 ++++++++ app/lib/activitypub/activity/create.rb | 2 +- app/models/account.rb | 14 ++++++++++++++ app/models/account_warning.rb | 2 +- app/models/admin/account_action.rb | 9 +++++++++ app/models/admin/action_log_filter.rb | 2 ++ app/policies/account_policy.rb | 8 ++++++++ app/serializers/activitypub/note_serializer.rb | 4 ++++ app/serializers/rest/status_serializer.rb | 8 ++++++++ app/views/admin/accounts/show.html.haml | 7 +++++++ app/views/statuses/_detailed_status.html.haml | 6 +++--- app/views/statuses/_simple_status.html.haml | 6 +++--- config/locales/en.yml | 10 ++++++++++ config/locales/ja.yml | 10 ++++++++++ config/locales/simple_form.en.yml | 1 + config/locales/simple_form.ja.yml | 1 + config/routes.rb | 2 ++ ...0200614002136_add_sensitized_to_accounts.rb | 5 +++++ db/schema.rb | 1 + .../api/v1/admin/accounts_controller_spec.rb | 18 ++++++++++++++++++ spec/models/admin/account_action_spec.rb | 8 ++++---- spec/policies/account_policy_spec.rb | 2 +- 24 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 db/migrate/20200614002136_add_sensitized_to_accounts.rb diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index b9b75727d..1dd7430e0 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -53,6 +53,13 @@ module Admin redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct) end + def unsensitive + authorize @account, :unsensitive? + @account.unsensitize! + log_action :unsensitive, @account + redirect_to admin_account_path(@account.id) + end + def unsilence authorize @account, :unsilence? @account.unsilence! diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb index 3af572f25..63cc521ed 100644 --- a/app/controllers/api/v1/admin/accounts_controller.rb +++ b/app/controllers/api/v1/admin/accounts_controller.rb @@ -22,6 +22,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController active pending disabled + sensitized silenced suspended username @@ -68,6 +69,13 @@ class Api::V1::Admin::AccountsController < Api::BaseController render json: @account, serializer: REST::Admin::AccountSerializer end + def unsensitive + authorize @account, :unsensitive? + @account.unsensitize! + log_action :unsensitive, @account + render json: @account, serializer: REST::Admin::AccountSerializer + end + def unsilence authorize @account, :unsilence? @account.unsilence! diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index a51597cf3..adb7918c5 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -117,6 +117,14 @@ module StatusesHelper end end + def sensitized?(status, account) + if !account.nil? && account.id == status.account_id + status.sensitive + else + status.account.sensitized? || status.sensitive + end + end + private def simplified_text(text) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index f275feefc..c77f237f9 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -111,7 +111,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity created_at: @object['published'], override_timestamps: @options[:override_timestamps], reply: @object['inReplyTo'].present?, - sensitive: @object['sensitive'] || false, + sensitive: @account.sensitized? || @object['sensitive'] || false, visibility: visibility_from_audience, thread: replied_to_status, conversation: conversation_from_uri(@object['conversation']), diff --git a/app/models/account.rb b/app/models/account.rb index 59d338f5a..d2112a13d 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -50,6 +50,7 @@ # avatar_storage_schema_version :integer # header_storage_schema_version :integer # devices_url :string +# sensitized_at :datetime # class Account < ApplicationRecord @@ -92,6 +93,7 @@ class Account < ApplicationRecord scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) } scope :silenced, -> { where.not(silenced_at: nil) } scope :suspended, -> { where.not(suspended_at: nil) } + scope :sensitized, -> { where.not(sensitized_at: nil) } scope :without_suspended, -> { where(suspended_at: nil) } scope :without_silenced, -> { where(silenced_at: nil) } scope :recent, -> { reorder(id: :desc) } @@ -234,6 +236,18 @@ class Account < ApplicationRecord end end + def sensitized? + sensitized_at.present? + end + + def sensitize!(date = Time.now.utc) + update!(sensitized_at: date) + end + + def unsensitize! + update!(sensitized_at: nil) + end + def memorialize! update!(memorial: true) end diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 157e6c04d..5efc924d5 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -13,7 +13,7 @@ # class AccountWarning < ApplicationRecord - enum action: %i(none disable silence suspend), _suffix: :action + enum action: %i(none disable sensitive silence suspend), _suffix: :action belongs_to :account, inverse_of: :account_warnings belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index c4ac09520..11ce737f3 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -8,6 +8,7 @@ class Admin::AccountAction TYPES = %w( none disable + sensitive silence suspend ).freeze @@ -64,6 +65,8 @@ class Admin::AccountAction case type when 'disable' handle_disable! + when 'sensitive' + handle_sensitive! when 'silence' handle_silence! when 'suspend' @@ -109,6 +112,12 @@ class Admin::AccountAction target_account.user&.disable! end + def handle_sensitive! + authorize(target_account, :sensitive?) + log_action(:sensitive, target_account) + target_account.sensitize! + end + def handle_silence! authorize(target_account, :silence?) log_action(:silence, target_account) diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index 0ba7e1609..3a1b67e06 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -35,9 +35,11 @@ class Admin::ActionLogFilter reopen_report: { target_type: 'Report', action: 'reopen' }.freeze, reset_password_user: { target_type: 'User', action: 'reset_password' }.freeze, resolve_report: { target_type: 'Report', action: 'resolve' }.freeze, + sensitive_account: { target_type: 'Account', action: 'sensitive' }.freeze, silence_account: { target_type: 'Account', action: 'silence' }.freeze, suspend_account: { target_type: 'Account', action: 'suspend' }.freeze, unassigned_report: { target_type: 'Report', action: 'unassigned' }.freeze, + unsensitive_account: { target_type: 'Account', action: 'unsensitive' }.freeze, unsilence_account: { target_type: 'Account', action: 'unsilence' }.freeze, unsuspend_account: { target_type: 'Account', action: 'unsuspend' }.freeze, update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index 1b105e92a..679119075 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -25,6 +25,14 @@ class AccountPolicy < ApplicationPolicy staff? end + def sensitive? + staff? && !record.user&.staff? + end + + def unsensitive? + staff? + end + def silence? staff? && !record.user&.staff? end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index f26fd93a4..6f9e1ca63 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -95,6 +95,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer ActivityPub::TagManager.instance.cc(object) end + def sensitive + object.account.sensitized? || object.sensitive + end + def virtual_tags object.active_mentions.to_a.sort_by(&:id) + object.tags + object.emojis end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 0109de882..bb6df90b7 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -58,6 +58,14 @@ class REST::StatusSerializer < ActiveModel::Serializer end end + def sensitive + if current_user? && current_user.account_id == object.account_id + object.sensitive + else + object.account.sensitized? || object.sensitive + end + end + def uri ActivityPub::TagManager.instance.uri_for(object) end diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index f0a216f6b..d5978eddd 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -69,6 +69,8 @@ = t('admin.accounts.confirming') - elsif @account.local? && !@account.user_approved? = t('admin.accounts.pending') + - elsif @account.sensitized? + = t('admin.accounts.sensitive') - else = t('admin.accounts.no_limits_imposed') .dashboard__counters__label= t 'admin.accounts.login_status' @@ -192,6 +194,11 @@ - else = link_to t('admin.accounts.disable'), new_admin_account_action_path(@account.id, type: 'disable'), class: 'button' if can?(:disable, @account.user) + - if @account.sensitized? + = link_to t('admin.accounts.undo_sensitized'), unsensitive_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsensitive, @account) + - elsif !@account.local? || @account.user_approved? + = link_to t('admin.accounts.sensitive'), new_admin_account_action_path(@account.id, type: 'sensitive'), class: 'button' if can?(:sensitive, @account) + - if @account.silenced? = link_to t('admin.accounts.undo_silenced'), unsilence_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsilence, @account) - elsif !@account.local? || @account.user_approved? diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index b3e9c44fc..a4dd8534f 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -29,17 +29,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 670, height: 380, detailed: true, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 670, height: 380, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 380, sensitive: sensitized?(status, current_account), standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index 7408749cc..192192700 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -35,17 +35,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 610, height: 343, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 610, height: 343, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 343, sensitive: sensitized?(status, current_account), autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do diff --git a/config/locales/en.yml b/config/locales/en.yml index 084006a2a..c962bc532 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -188,6 +188,8 @@ en: search: Search search_same_email_domain: Other users with the same e-mail domain search_same_ip: Other users with the same IP + sensitive: Sensitive + sensitized: marked as sensitive shared_inbox_url: Shared inbox URL show: created_reports: Made reports @@ -202,6 +204,7 @@ en: time_in_queue: Waiting in queue %{time} title: Accounts unconfirmed_email: Unconfirmed email + undo_sensitized: Undo sensitive undo_silenced: Undo silence undo_suspension: Undo suspension unsilenced_msg: Successfully unlimited %{username}'s account @@ -243,9 +246,11 @@ en: reopen_report: Reopen Report reset_password_user: Reset Password resolve_report: Resolve Report + sensitive_account: Mark the media in your account as sensitive silence_account: Silence Account suspend_account: Suspend Account unassigned_report: Unassign Report + unsensitive_account: Unmark the media in your account as sensitive unsilence_account: Unsilence Account unsuspend_account: Unsuspend Account update_announcement: Update Announcement @@ -281,9 +286,11 @@ en: reopen_report: "%{name} reopened report %{target}" reset_password_user: "%{name} reset password of user %{target}" resolve_report: "%{name} resolved report %{target}" + sensitive_account: "%{name} marked %{target}'s media as sensitive" silence_account: "%{name} silenced %{target}'s account" suspend_account: "%{name} suspended %{target}'s account" unassigned_report: "%{name} unassigned report %{target}" + unsensitive_account: "%{name} unmarked %{target}'s media as sensitive" unsilence_account: "%{name} unsilenced %{target}'s account" unsuspend_account: "%{name} unsuspended %{target}'s account" update_announcement: "%{name} updated announcement %{target}" @@ -1339,6 +1346,7 @@ en: warning: explanation: disable: You can no longer login to your account or use it in any other way, but your profile and other data remains intact. + sensitive: Your uploaded media files and linked media will be treated as sensitive. silence: You can still use your account but only people who are already following you will see your toots on this server, and you may be excluded from various public listings. However, others may still manually follow you. suspend: You can no longer use your account, and your profile and other data are no longer accessible. You can still login to request a backup of your data until the data is fully removed, but we will retain some data to prevent you from evading the suspension. get_in_touch: You can reply to this e-mail to get in touch with the staff of %{instance}. @@ -1347,11 +1355,13 @@ en: subject: disable: Your account %{acct} has been frozen none: Warning for %{acct} + sensitive: Your account %{acct} posting media has been marked as sensitive silence: Your account %{acct} has been limited suspend: Your account %{acct} has been suspended title: disable: Account frozen none: Warning + sensitive: Your media has been marked as sensitive silence: Account limited suspend: Account suspended welcome: diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fb6255546..fd9ec9427 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -172,6 +172,8 @@ ja: search: 検索 search_same_email_domain: åŒã˜ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’使用ã—ã¦ã„るユーザー search_same_ip: åŒã˜ IP ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’検索 + sensitive: é–²è¦§æ³¨æ„ + sensitized: é–²è¦§æ³¨æ„æ¸ˆã¿ shared_inbox_url: Shared inbox URL show: created_reports: ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ä½œã‚‰ã‚ŒãŸé€šå ± @@ -184,6 +186,7 @@ ja: time_in_queue: "%{time} å¾…ã¡" title: アカウント unconfirmed_email: 確èªå¾…ã¡ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ + undo_sensitized: 閲覧注æ„ã‹ã‚‰æˆ»ã™ undo_silenced: サイレンスã‹ã‚‰æˆ»ã™ undo_suspension: åœæ¢ã‹ã‚‰æˆ»ã™ unsubscribe: è³¼èªã®è§£é™¤ @@ -220,9 +223,11 @@ ja: reopen_report: é€šå ±ã‚’å†åº¦é–‹ã reset_password_user: パスワードをリセット resolve_report: é€šå ±ã‚’è§£æ±ºæ¸ˆã¿ã«ã™ã‚‹ + sensitive_account: アカウントã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’閲覧注æ„ã«ãƒžãƒ¼ã‚¯ silence_account: アカウントをサイレンス suspend_account: ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’åœæ¢ unassigned_report: é€šå ±ã®æ‹…当を解除 + unsensitive_account: アカウントã®ãƒ¡ãƒ‡ã‚£ã‚¢ã®é–²è¦§æ³¨æ„マークを解除 unsilence_account: アカウントã®ã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã‚’解除 unsuspend_account: アカウントã®åœæ¢ã‚’解除 update_announcement: ãŠçŸ¥ã‚‰ã›ã‚’æ›´æ–° @@ -256,9 +261,11 @@ ja: reopen_report: "%{name} ã•ã‚“ãŒé€šå ± %{target} ã‚’å†ã³é–‹ãã¾ã—ãŸ" reset_password_user: "%{name} ã•ん㌠%{target} ã•ã‚“ã®ãƒ‘スワードをリセットã—ã¾ã—ãŸ" resolve_report: "%{name} ã•ã‚“ãŒé€šå ± %{target} を解決済ã¿ã«ã—ã¾ã—ãŸ" + sensitive_account: "%{name} ã•ん㌠%{target} ã•ã‚“ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’閲覧注æ„ã«ãƒžãƒ¼ã‚¯ã—ã¾ã—ãŸ" silence_account: "%{name} ã•ん㌠%{target} ã•んをサイレンスã«ã—ã¾ã—ãŸ" suspend_account: "%{name} ã•ん㌠%{target} ã•ã‚“ã‚’åœæ¢ã—ã¾ã—ãŸ" unassigned_report: "%{name} ã•ã‚“ãŒé€šå ± %{target} ã®æ‹…当を外ã—ã¾ã—ãŸ" + unsensitive_account: "%{name} ã•ん㌠%{target} ã•ã‚“ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã®é–²è¦§æ³¨æ„を解除ã—ã¾ã—ãŸ" unsilence_account: "%{name} ã•ん㌠%{target} ã•ã‚“ã®ã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã‚’解除ã—ã¾ã—ãŸ" unsuspend_account: "%{name} ã•ん㌠%{target} ã•ã‚“ã®åœæ¢ã‚’解除ã—ã¾ã—ãŸ" update_announcement: "%{name} ã•ã‚“ãŒãŠçŸ¥ã‚‰ã› %{target} ã‚’æ›´æ–°ã—ã¾ã—ãŸ" @@ -1271,6 +1278,7 @@ ja: warning: explanation: disable: アカウントãŒå‡çµã•れã¦ã„ã‚‹é–“ã€ãƒ‡ãƒ¼ã‚¿ã¯ãã®ã¾ã¾æ®‹ã‚Šã¾ã™ãŒã€å‡çµãŒè§£é™¤ã•れるã¾ã§ã¯ä½•ã®æ“作もã§ãã¾ã›ã‚“。 + sensitive: ã‚ãªãŸã®ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã—ãŸãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ァイルã¨ãƒªãƒ³ã‚¯å…ˆã®ãƒ¡ãƒ‡ã‚£ã‚¢ã¯ã€é–²è¦§æ³¨æ„ã¨ã—ã¦æ‰±ã‚れã¾ã™ã€‚ silence: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯åˆ¶é™ã•れã¦ã„ã¾ã™ãŒã€ã‚ãªãŸã‚’フォãƒãƒ¼ã—ã¦ã„るユーザーã®ã¿ã€ã“ã®ã‚µãƒ¼ãƒãƒ¼ä¸Šã®æŠ•稿を見るã“ã¨ãŒã§ãã¾ã™ã€‚ãã—ã¦ã‚ãªãŸã¯æ§˜ã€…ãªå…¬é–‹ãƒªã‚¹ãƒˆã‹ã‚‰é™¤å¤–ã•れるã‹ã‚‚ã—れã¾ã›ã‚“。ãŸã ã—ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ‰‹å‹•ã§ã‚ãªãŸã‚’フォãƒãƒ¼ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ suspend: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯åœæ¢ã•れã¦ã„ã¾ã™ã€‚ã‚ãªãŸã®æŠ•稿ã¨ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•れãŸãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ァイルã¯ã€ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¨ã‚ãªãŸã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ãŒå‚åŠ ã—ã¦ã„ãŸã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰å®Œå…¨ã«å‰Šé™¤ã•れã¾ã—ãŸã€‚ get_in_touch: ã“ã®ãƒ¡ãƒ¼ãƒ«ã«è¿”ä¿¡ã™ã‚‹ã“ã¨ã§ %{instance} ã®ã‚¹ã‚¿ãƒƒãƒ•ã¨é€£çµ¡ã‚’å–ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ @@ -1279,11 +1287,13 @@ ja: subject: disable: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ %{acct} ã¯å‡çµã•れã¾ã—㟠none: "%{acct} ã«å¯¾ã™ã‚‹è¦å‘Š" + sensitive: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ %{acct} ã®æŠ•ç¨¿ãƒ¡ãƒ‡ã‚£ã‚¢ã¯é–²è¦§æ³¨æ„ã¨ãƒžãƒ¼ã‚¯ã•れã¾ã—㟠silence: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ %{acct} ã¯ã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã«ã•れã¾ã—㟠suspend: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ %{acct} ã¯åœæ¢ã•れã¾ã—㟠title: disable: アカウントãŒå‡çµã•れã¾ã—㟠none: è¦å‘Š + sensitive: ã‚ãªãŸã®ãƒ¡ãƒ‡ã‚£ã‚¢ãŒé–²è¦§æ³¨æ„ã¨ãƒžãƒ¼ã‚¯ã•れã¾ã—㟠silence: アカウントãŒã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã«ã•れã¾ã—㟠suspend: アカウントãŒåœæ¢ã•れã¾ã—㟠welcome: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index b69487953..46a4759a8 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -100,6 +100,7 @@ en: types: disable: Freeze none: Send a warning + sensitive: Sensitive silence: Limit suspend: Suspend warning_preset_id: Use a warning preset diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index bbc0b5fd7..00f469b87 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -91,6 +91,7 @@ ja: types: disable: ãƒã‚°ã‚¤ãƒ³ã‚’無効化 none: 何もã—ãªã„ + sensitive: é–²è¦§æ³¨æ„ silence: サイレンス suspend: åœæ¢ã—アカウントã®ãƒ‡ãƒ¼ã‚¿ã‚’æ’ä¹…çš„ã«å‰Šé™¤ã™ã‚‹ warning_preset_id: プリセットè¦å‘Šæ–‡ã‚’使用 diff --git a/config/routes.rb b/config/routes.rb index e0ef23723..54c76799c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -236,6 +236,7 @@ Rails.application.routes.draw do resources :accounts, only: [:index, :show, :destroy] do member do post :enable + post :unsensitive post :unsilence post :unsuspend post :redownload @@ -476,6 +477,7 @@ Rails.application.routes.draw do resources :accounts, only: [:index, :show, :destroy] do member do post :enable + post :unsensitive post :unsilence post :unsuspend post :approve diff --git a/db/migrate/20200614002136_add_sensitized_to_accounts.rb b/db/migrate/20200614002136_add_sensitized_to_accounts.rb new file mode 100644 index 000000000..bc2dfcb63 --- /dev/null +++ b/db/migrate/20200614002136_add_sensitized_to_accounts.rb @@ -0,0 +1,5 @@ +class AddSensitizedToAccounts < ActiveRecord::Migration[5.2] + def change + add_column :accounts, :sensitized_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 262e25b3b..5ff6b6300 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -189,6 +189,7 @@ ActiveRecord::Schema.define(version: 2020_10_08_220312) do t.integer "avatar_storage_schema_version" t.integer "header_storage_schema_version" t.string "devices_url" + t.datetime "sensitized_at" 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 t.index "lower((username)::text), COALESCE(lower((domain)::text), ''::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id" diff --git a/spec/controllers/api/v1/admin/accounts_controller_spec.rb b/spec/controllers/api/v1/admin/accounts_controller_spec.rb index f3f9946ba..89cadb222 100644 --- a/spec/controllers/api/v1/admin/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/admin/accounts_controller_spec.rb @@ -127,6 +127,24 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do end end + describe 'POST #unsensitive' do + before do + account.touch(:sensitized_at) + post :unsensitive, params: { id: account.id } + end + + it_behaves_like 'forbidden for wrong scope', 'write:statuses' + it_behaves_like 'forbidden for wrong role', 'user' + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'unsensitives account' do + expect(account.reload.sensitized?).to be false + end + end + describe 'POST #unsilence' do before do account.touch(:silenced_at) diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb index 87fc28500..2366b9ca4 100644 --- a/spec/models/admin/account_action_spec.rb +++ b/spec/models/admin/account_action_spec.rb @@ -115,16 +115,16 @@ RSpec.describe Admin::AccountAction, type: :model do context 'account.local?' do let(:account) { Fabricate(:account, domain: nil) } - it 'returns ["none", "disable", "silence", "suspend"]' do - expect(subject).to eq %w(none disable silence suspend) + it 'returns ["none", "disable", "sensitive", "silence", "suspend"]' do + expect(subject).to eq %w(none disable sensitive silence suspend) end end context '!account.local?' do let(:account) { Fabricate(:account, domain: 'hoge.com') } - it 'returns ["silence", "suspend"]' do - expect(subject).to eq %w(silence suspend) + it 'returns ["sensitive", "silence", "suspend"]' do + expect(subject).to eq %w(sensitive silence suspend) end end end diff --git a/spec/policies/account_policy_spec.rb b/spec/policies/account_policy_spec.rb index 6648b0888..d27e9d5b0 100644 --- a/spec/policies/account_policy_spec.rb +++ b/spec/policies/account_policy_spec.rb @@ -8,7 +8,7 @@ RSpec.describe AccountPolicy do let(:admin) { Fabricate(:user, admin: true).account } let(:john) { Fabricate(:user).account } - permissions :index?, :show?, :unsuspend?, :unsilence?, :remove_avatar?, :remove_header? do + permissions :index?, :show?, :unsuspend?, :unsensitive?, :unsilence?, :remove_avatar?, :remove_header? do context 'staff' do it 'permits' do expect(subject).to permit(admin) -- GitLab