users: move account deletion endpoint to /users/:id/deactivate.

Move the account deletion endpoint from /maintenance/users/deletion to either:

* https://danbooru.donmai.us/users/deactivate
* https://danbooru.donmai.us/users/:id/deactivate

This incidentally allows the Owner-level user to deactivate accounts belonging to other users. This
is meant for things like deactivating inactive accounts with invalid or abusive names. This is
limited to accounts below Gold level for security.
This commit is contained in:
evazion
2022-11-05 18:31:49 -05:00
parent 59872d2ed5
commit 3ffde5b23d
10 changed files with 163 additions and 112 deletions

View File

@@ -1,26 +0,0 @@
# frozen_string_literal: true
module Maintenance
module User
class DeletionsController < ApplicationController
respond_to :html, :json, :xml
def show
end
def destroy
deletion = UserDeletion.new(user: CurrentUser.user, deleter: CurrentUser.user, password: params.dig(:user, :password), request: request)
deletion.delete!
if deletion.errors.none?
session.delete(:user_id)
flash[:notice] = "Your account has been deactivated"
respond_with(deletion, location: posts_path)
else
flash[:notice] = deletion.errors.full_messages.join("; ")
redirect_to maintenance_user_deletion_path
end
end
end
end
end

View File

@@ -114,6 +114,32 @@ class UsersController < ApplicationController
end
end
def deactivate
if params[:id].present?
@user = authorize User.find(params[:id])
else
@user = authorize CurrentUser.user
end
respond_with(@user)
end
def destroy
@user = authorize User.find(params[:id])
user_deletion = UserDeletion.new(user: @user, deleter: CurrentUser.user, password: params.dig(:user, :password), request: request)
user_deletion.delete!
if user_deletion.errors.none?
session.delete(:user_id)
flash[:notice] = "Your account has been deactivated"
respond_with(user_deletion, location: posts_path)
else
flash[:notice] = user_deletion.errors.full_messages.join("; ")
redirect_to deactivate_user_path(@user)
end
end
def custom_style
@custom_css = CurrentUser.user.custom_css
expires_in 10.years

View File

@@ -13,6 +13,14 @@ class UserPolicy < ApplicationPolicy
record.id == user.id || user.is_admin?
end
def deactivate?
(record.id == user.id && !user.is_anonymous?) || user.is_owner?
end
def destroy?
deactivate?
end
def promote?
user.is_moderator?
end

View File

@@ -1,40 +0,0 @@
<% page_title "Deactivate Account" %>
<div id="c-maintenance-user-deletions">
<div id="a-show" class="prose">
<h1>Deactivate Account</h1>
<p>
You can deactivate your <%= Danbooru.config.app_name %> account using
the form below. Deactivating your account will do the following things:
</p>
<ul>
<li>Change your name to a generic name</li>
<li>Delete your favorites</li>
<li>Delete your saved searches</li>
<li>Delete your password and email address</li>
<li>Delete your account settings</li>
</ul>
<p>
The following things will <strong>not</strong> be deleted:
</p>
<ul>
<li>Posts you've uploaded</li>
<li>Comments, forum posts, and private messages</li>
<li>Tag edits, wiki edits, translation notes, or any other contributions to the site</li>
</ul>
<p>
Enter your password below to deactivate your account. This cannot be
undone. Your account cannot be recovered after it is deactivated.
</p>
<%= edit_form_for(:user, url: maintenance_user_deletion_path, method: :delete) do |f| %>
<%= f.input :password %>
<%= f.submit "Deactivate account", "data-confirm": "Are you sure you want to deactivate your account? This cannot be undone" %>
<% end %>
</div>
</div>

View File

@@ -353,8 +353,8 @@
<p><em>Summary: You may deactivate your account, but we may
retain your IP address and your public contributions to the Site.</em></p>
<p>If you would like to delete your account, you may request your
account to be deactivated <%= link_to "here", maintenance_user_deletion_path %>.</p>
<p>If you wish to to close your <%= @app_name %> account, you may deactivate your account
<%= link_to "here", deactivate_users_path %>.</p>
<p>When your account is deactivated, we will delete your non-public personal
information from our active systems, including your email address,

View File

@@ -0,0 +1,47 @@
<% page_title "Deactivate Account" %>
<%= render "secondary_links" %>
<div id="c-users">
<div id="a-deactivate">
<% if @user == CurrentUser.user %>
<h1>Deactivate Account</h1>
<% else %>
<h1>Deactivate Account: <%= link_to_user @user %></h1>
<% end %>
<div class="prose mb-4">
<p>
You can deactivate your <%= Danbooru.config.app_name %> account by entering your password below. Deactivating
your account will do the following things:
</p>
<ul>
<li>Change your username to a generic username (<i>user_<%= @user.id %></i>).</li>
<li>Delete your password, email address, and account settings.</li>
<li>Delete your favorites.</li>
<li>Delete your saved searches.</li>
</ul>
<p>
The following things will <strong>not</strong> be deleted:
</p>
<ul>
<li>Posts you've uploaded.</li>
<li>Your comments, forum posts, and private messages.</li>
<li>Your tag edits, wiki edits, translation notes, and any other contributions you've made to the site.</li>
<li>Your login history, including your IP address and geographic location. This is kept for moderation purposes.</li>
</ul>
<p>
Enter your password below to deactivate your account. This cannot be
undone. Your account cannot be recovered after it is deactivated.
</p>
</div>
<%= edit_form_for(:user, url: user_path(@user), method: :delete) do |f| %>
<%= f.input :password %>
<%= f.submit "Deactivate account", "data-confirm": "Are you sure you want to deactivate your account? This cannot be undone" %>
<% end %>
</div>
</div>

View File

@@ -53,7 +53,7 @@
<div class="input">
<label>Account</label>
<p>
<%= link_to "Deactivate account", maintenance_user_deletion_path, id: "delete-account" %>
<%= link_to "Deactivate account", deactivate_user_path(@user), id: "delete-account" %>
(<em>requires confirmation</em>)
</p>
</div>

View File

@@ -50,7 +50,6 @@ Rails.application.routes.draw do
namespace :user do
resource :count_fixes, only: [:new, :create]
resource :email_notification, only: [:show, :create, :destroy]
resource :deletion, :only => [:show, :destroy]
end
end
@@ -274,6 +273,8 @@ Rails.application.routes.draw do
get :change_name, on: :member, to: "user_name_change_requests#new"
get :custom_style, on: :collection
get :deactivate, on: :member # /users/:id/deactivate
get :deactivate, on: :collection # /users/deactivate
end
get "/upgrade", to: "user_upgrades#new", as: "new_user_upgrade"
get "/user_upgrades/new", to: redirect("/upgrade")
@@ -354,6 +355,7 @@ Rails.application.routes.draw do
get "/static/contact", to: redirect("/contact")
get "/user_upgrade/new", to: redirect("/upgrade")
get "/delayed_jobs", to: redirect("/jobs")
get "/maintenance/user/deletion", to: redirect("/users/deactivate")
get "/mock/recommender/recommend/:user_id" => "mock_services#recommender_recommend", as: "mock_recommender_recommend"
get "/mock/recommender/similiar/:post_id" => "mock_services#recommender_similar", as: "mock_recommender_similar"

View File

@@ -1,42 +0,0 @@
require "test_helper"
module Maintenance
module User
class DeletionsControllerTest < ActionDispatch::IntegrationTest
context "in all cases" do
setup do
@user = create(:user)
end
context "#show" do
should "render" do
get_auth maintenance_user_deletion_path, @user
assert_response :success
end
end
context "#destroy" do
should "delete the user when given the correct password" do
delete_auth maintenance_user_deletion_path, @user, params: { user: { password: "password" }}
assert_redirected_to posts_path
assert_equal(true, @user.reload.is_deleted?)
assert_equal("Your account has been deactivated", flash[:notice])
assert_nil(session[:user_id])
assert_equal(true, @user.user_events.user_deletion.exists?)
end
should "not delete the user when given an incorrect password" do
delete_auth maintenance_user_deletion_path, @user, params: { user: { password: "hunter2" }}
assert_redirected_to maintenance_user_deletion_path
assert_equal(false, @user.reload.is_deleted?)
assert_equal("Password is incorrect", flash[:notice])
assert_equal(@user.id, session[:user_id])
assert_equal(false, @user.user_events.user_deletion.exists?)
end
end
end
end
end
end

View File

@@ -102,6 +102,82 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
end
end
context "#deactivate action" do
should "render /users/:id/deactivate for the current user" do
get_auth deactivate_user_path(@user), @user
assert_response :success
end
should "render /users/:id/deactivate for the Owner user" do
get_auth deactivate_user_path(@user), create(:owner)
assert_response :success
end
should "not render /users/:id/deactivate for a different user" do
get_auth deactivate_user_path(@user), create(:user)
assert_response 403
end
should "render /users/deactivate for a logged-in user" do
get_auth deactivate_users_path, @user
assert_response :success
end
should "not render /users/deactivate for a logged-out user" do
get deactivate_users_path
assert_response 403
end
should "redirect /maintenance/user/deletion to /users/deactivate" do
get "/maintenance/user/deletion"
assert_redirected_to deactivate_users_path
end
end
context "#destroy action" do
should "delete the user when given the correct password" do
delete_auth user_path(@user), @user, params: { user: { password: "password" }}
assert_redirected_to posts_path
assert_equal(true, @user.reload.is_deleted?)
assert_equal("Your account has been deactivated", flash[:notice])
assert_nil(session[:user_id])
assert_equal(true, @user.user_events.user_deletion.exists?)
end
should "not delete the user when given an incorrect password" do
delete_auth user_path(@user), @user, params: { user: { password: "hunter2" }}
assert_redirected_to deactivate_user_path(@user)
assert_equal(false, @user.reload.is_deleted?)
assert_equal("Password is incorrect", flash[:notice])
assert_equal(@user.id, session[:user_id])
assert_equal(false, @user.user_events.user_deletion.exists?)
end
should "allow the Owner to delete other users" do
delete_auth user_path(@user), create(:owner)
assert_redirected_to posts_path
assert_equal(true, @user.reload.is_deleted?)
assert_equal("Your account has been deactivated", flash[:notice])
assert_nil(session[:user_id])
assert_equal(true, @user.user_events.user_deletion.exists?)
end
should "not allow users to delete other users" do
delete_auth user_path(@user), create(:user), params: { user: { password: "password" }}
assert_response 403
end
should "not allow logged-out users to delete other users" do
delete user_path(@user), params: { user: { password: "password" }}
assert_response 403
end
end
context "custom_style action" do
should "work" do
@user.update!(custom_style: "span { color: red; }")