summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-11-07 19:06:44 +0100
committerGitHub <noreply@github.com>2017-11-07 19:06:44 +0100
commit1032f3994fdbd61c2f517057261ddc3559199b6b (patch)
treed039701515efc050dbf91124e8d32da2014498fb /app
parentcbbeec05be5cd0930a7be73bf673acc8d8105c12 (diff)
Add ability to disable login and mark accounts as memorial (#5615)
Fix #5597
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/accounts_controller.rb22
-rw-r--r--app/controllers/admin/suspensions_controller.rb2
-rw-r--r--app/javascript/styles/mastodon/landing_strip.scss7
-rw-r--r--app/mailers/notification_mailer.rb18
-rw-r--r--app/mailers/user_mailer.rb6
-rw-r--r--app/models/account.rb15
-rw-r--r--app/models/user.rb18
-rw-r--r--app/services/suspend_account_service.rb25
-rw-r--r--app/views/accounts/_header.html.haml33
-rw-r--r--app/views/accounts/show.html.haml4
-rw-r--r--app/views/admin/accounts/show.html.haml11
-rw-r--r--app/workers/admin/suspension_worker.rb2
12 files changed, 125 insertions, 38 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index ffa4dc850f0..7503b880d2a 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -2,8 +2,9 @@
module Admin
class AccountsController < BaseController
- before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload]
+ before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :enable, :disable, :memorialize]
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
+ before_action :require_local_account!, only: [:enable, :disable, :memorialize]
def index
@accounts = filtered_accounts.page(params[:page])
@@ -24,6 +25,21 @@ module Admin
redirect_to admin_account_path(@account.id)
end
+ def memorialize
+ @account.memorialize!
+ redirect_to admin_account_path(@account.id)
+ end
+
+ def enable
+ @account.user.enable!
+ redirect_to admin_account_path(@account.id)
+ end
+
+ def disable
+ @account.user.disable!
+ redirect_to admin_account_path(@account.id)
+ end
+
def redownload
@account.reset_avatar!
@account.reset_header!
@@ -42,6 +58,10 @@ module Admin
redirect_to admin_account_path(@account.id) if @account.local?
end
+ def require_local_account!
+ redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
+ end
+
def filtered_accounts
AccountFilter.new(filter_params).results
end
diff --git a/app/controllers/admin/suspensions_controller.rb b/app/controllers/admin/suspensions_controller.rb
index 5d9048d94df..5eaf1a2e914 100644
--- a/app/controllers/admin/suspensions_controller.rb
+++ b/app/controllers/admin/suspensions_controller.rb
@@ -10,7 +10,7 @@ module Admin
end
def destroy
- @account.update(suspended: false)
+ @account.unsuspend!
redirect_to admin_accounts_path
end
diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss
index 15ff8491285..0bf9daafd4b 100644
--- a/app/javascript/styles/mastodon/landing_strip.scss
+++ b/app/javascript/styles/mastodon/landing_strip.scss
@@ -1,4 +1,5 @@
-.landing-strip {
+.landing-strip,
+.memoriam-strip {
background: rgba(darken($ui-base-color, 7%), 0.8);
color: $ui-primary-color;
font-weight: 400;
@@ -29,3 +30,7 @@
margin-bottom: 0;
}
}
+
+.memoriam-strip {
+ background: rgba($base-shadow-color, 0.7);
+}
diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb
index 80c9d8ccfae..d79f26366ae 100644
--- a/app/mailers/notification_mailer.rb
+++ b/app/mailers/notification_mailer.rb
@@ -7,6 +7,8 @@ class NotificationMailer < ApplicationMailer
@me = recipient
@status = notification.target_status
+ return if @me.user.disabled?
+
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
mail to: @me.user.email, subject: I18n.t('notification_mailer.mention.subject', name: @status.account.acct)
@@ -17,6 +19,8 @@ class NotificationMailer < ApplicationMailer
@me = recipient
@account = notification.from_account
+ return if @me.user.disabled?
+
locale_for_account(@me) do
mail to: @me.user.email, subject: I18n.t('notification_mailer.follow.subject', name: @account.acct)
end
@@ -27,6 +31,8 @@ class NotificationMailer < ApplicationMailer
@account = notification.from_account
@status = notification.target_status
+ return if @me.user.disabled?
+
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
mail to: @me.user.email, subject: I18n.t('notification_mailer.favourite.subject', name: @account.acct)
@@ -38,6 +44,8 @@ class NotificationMailer < ApplicationMailer
@account = notification.from_account
@status = notification.target_status
+ return if @me.user.disabled?
+
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
mail to: @me.user.email, subject: I18n.t('notification_mailer.reblog.subject', name: @account.acct)
@@ -48,6 +56,8 @@ class NotificationMailer < ApplicationMailer
@me = recipient
@account = notification.from_account
+ return if @me.user.disabled?
+
locale_for_account(@me) do
mail to: @me.user.email, subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
end
@@ -59,15 +69,11 @@ class NotificationMailer < ApplicationMailer
@notifications = Notification.where(account: @me, activity_type: 'Mention').where('created_at > ?', @since)
@follows_since = Notification.where(account: @me, activity_type: 'Follow').where('created_at > ?', @since).count
- return if @notifications.empty?
+ return if @me.user.disabled? || @notifications.empty?
locale_for_account(@me) do
mail to: @me.user.email,
- subject: I18n.t(
- :subject,
- scope: [:notification_mailer, :digest],
- count: @notifications.size
- )
+ subject: I18n.t(:subject, scope: [:notification_mailer, :digest], count: @notifications.size)
end
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index c475a9911b1..bdb29ebadee 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -10,6 +10,8 @@ class UserMailer < Devise::Mailer
@token = token
@instance = Rails.configuration.x.local_domain
+ return if @resource.disabled?
+
I18n.with_locale(@resource.locale || I18n.default_locale) do
mail to: @resource.unconfirmed_email.blank? ? @resource.email : @resource.unconfirmed_email, subject: I18n.t('devise.mailer.confirmation_instructions.subject', instance: @instance)
end
@@ -20,6 +22,8 @@ class UserMailer < Devise::Mailer
@token = token
@instance = Rails.configuration.x.local_domain
+ return if @resource.disabled?
+
I18n.with_locale(@resource.locale || I18n.default_locale) do
mail to: @resource.email, subject: I18n.t('devise.mailer.reset_password_instructions.subject')
end
@@ -29,6 +33,8 @@ class UserMailer < Devise::Mailer
@resource = user
@instance = Rails.configuration.x.local_domain
+ return if @resource.disabled?
+
I18n.with_locale(@resource.locale || I18n.default_locale) do
mail to: @resource.email, subject: I18n.t('devise.mailer.password_change.subject')
end
diff --git a/app/models/account.rb b/app/models/account.rb
index 3dc2a95ab63..1142e7c7944 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -41,6 +41,7 @@
# shared_inbox_url :string default(""), not null
# followers_url :string default(""), not null
# protocol :integer default("ostatus"), not null
+# memorial :boolean default(FALSE), not null
#
class Account < ApplicationRecord
@@ -150,6 +151,20 @@ class Account < ApplicationRecord
ResolveRemoteAccountService.new.call(acct)
end
+ def unsuspend!
+ transaction do
+ user&.enable! if local?
+ update!(suspended: false)
+ end
+ end
+
+ def memorialize!
+ transaction do
+ user&.disable! if local?
+ update!(memorial: true)
+ end
+ end
+
def keypair
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 325e27f4415..836d54d1531 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -5,7 +5,6 @@
#
# id :integer not null, primary key
# email :string default(""), not null
-# account_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# encrypted_password :string default(""), not null
@@ -31,10 +30,13 @@
# last_emailed_at :datetime
# otp_backup_codes :string is an Array
# filtered_languages :string default([]), not null, is an Array
+# account_id :integer not null
+# disabled :boolean default(FALSE), not null
#
class User < ApplicationRecord
include Settings::Extend
+
ACTIVE_DURATION = 14.days
devise :registerable, :recoverable,
@@ -72,12 +74,26 @@ class User < ApplicationRecord
confirmed_at.present?
end
+ def disable!
+ update!(disabled: true,
+ last_sign_in_at: current_sign_in_at,
+ current_sign_in_at: nil)
+ end
+
+ def enable!
+ update!(disabled: false)
+ end
+
def disable_two_factor!
self.otp_required_for_login = false
otp_backup_codes&.clear
save!
end
+ def active_for_authentication?
+ super && !disabled?
+ end
+
def setting_default_privacy
settings.default_privacy || (account.locked? ? 'private' : 'public')
end
diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb
index 983c5495b60..5b37ba9ba7f 100644
--- a/app/services/suspend_account_service.rb
+++ b/app/services/suspend_account_service.rb
@@ -1,22 +1,27 @@
# frozen_string_literal: true
class SuspendAccountService < BaseService
- def call(account, remove_user = false)
+ def call(account, options = {})
@account = account
+ @options = options
- purge_user if remove_user
- purge_profile
- purge_content
- unsubscribe_push_subscribers
+ purge_user!
+ purge_profile!
+ purge_content!
+ unsubscribe_push_subscribers!
end
private
- def purge_user
- @account.user.destroy
+ def purge_user!
+ if @options[:remove_user]
+ @account.user&.destroy
+ else
+ @account.user&.disable!
+ end
end
- def purge_content
+ def purge_content!
@account.statuses.reorder(nil).find_in_batches do |statuses|
BatchedRemoveStatusService.new.call(statuses)
end
@@ -33,7 +38,7 @@ class SuspendAccountService < BaseService
end
end
- def purge_profile
+ def purge_profile!
@account.suspended = true
@account.display_name = ''
@account.note = ''
@@ -42,7 +47,7 @@ class SuspendAccountService < BaseService
@account.save!
end
- def unsubscribe_push_subscribers
+ def unsubscribe_push_subscribers!
destroy_all(@account.subscriptions)
end
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 08c3891d275..5530fcc200a 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -1,21 +1,22 @@
.card.h-card.p-author{ style: "background-image: url(#{account.header.url(:original)})" }
.card__illustration
- - if user_signed_in? && current_account.id != account.id && !current_account.requested?(account)
- .controls
- - if current_account.following?(account)
- = link_to account_unfollow_path(account), data: { method: :post }, class: 'icon-button' do
- = fa_icon 'user-times'
- = t('accounts.unfollow')
- - else
- = link_to account_follow_path(account), data: { method: :post }, class: 'icon-button' do
- = fa_icon 'user-plus'
- = t('accounts.follow')
- - elsif !user_signed_in?
- .controls
- .remote-follow
- = link_to account_remote_follow_path(account), class: 'icon-button' do
- = fa_icon 'user-plus'
- = t('accounts.remote_follow')
+ - unless account.memorial?
+ - if user_signed_in? && current_account.id != account.id && !current_account.requested?(account)
+ .controls
+ - if current_account.following?(account)
+ = link_to account_unfollow_path(account), data: { method: :post }, class: 'icon-button' do
+ = fa_icon 'user-times'
+ = t('accounts.unfollow')
+ - else
+ = link_to account_follow_path(account), data: { method: :post }, class: 'icon-button' do
+ = fa_icon 'user-plus'
+ = t('accounts.follow')
+ - elsif !user_signed_in?
+ .controls
+ .remote-follow
+ = link_to account_remote_follow_path(account), class: 'icon-button' do
+ = fa_icon 'user-plus'
+ = t('accounts.remote_follow')
.avatar= image_tag account.avatar.url(:original), class: 'u-photo'
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index 6c90b2c04f8..fd8ad5530be 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -12,7 +12,9 @@
= opengraph 'og:type', 'profile'
= render 'og', account: @account, url: short_account_url(@account, only_path: false)
-- if show_landing_strip?
+- if @account.memorial?
+ .memoriam-strip= t('in_memoriam_html')
+- elsif show_landing_strip?
= render partial: 'shared/landing_strip', locals: { account: @account }
.h-feed
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 1f5c8fcf53c..b5ce56dbc3d 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -19,6 +19,15 @@
%th= t('admin.accounts.email')
%td= @account.user_email
%tr
+ %th= t('admin.accounts.login_status')
+ %td
+ - if @account.user&.disabled?
+ = t('admin.accounts.disabled')
+ = table_link_to 'unlock', t('admin.accounts.enable'), enable_admin_account_path(@account.id), method: :post
+ - else
+ = t('admin.accounts.enabled')
+ = table_link_to 'lock', t('admin.accounts.disable'), disable_admin_account_path(@account.id), method: :post
+ %tr
%th= t('admin.accounts.most_recent_ip')
%td= @account.user_current_sign_in_ip
%tr
@@ -65,6 +74,8 @@
= link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button'
- if @account.user&.otp_required_for_login?
= link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button'
+ - unless @account.memorial?
+ = link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'
- else
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(@account.id), method: :post, class: 'button'
diff --git a/app/workers/admin/suspension_worker.rb b/app/workers/admin/suspension_worker.rb
index 6338b1130d9..e41465ccca0 100644
--- a/app/workers/admin/suspension_worker.rb
+++ b/app/workers/admin/suspension_worker.rb
@@ -6,6 +6,6 @@ class Admin::SuspensionWorker
sidekiq_options queue: 'pull'
def perform(account_id, remove_user = false)
- SuspendAccountService.new.call(Account.find(account_id), remove_user)
+ SuspendAccountService.new.call(Account.find(account_id), remove_user: remove_user)
end
end