From 3ffde5b23d09f1c9a54fa08980a047968082abb6 Mon Sep 17 00:00:00 2001 From: evazion Date: Sat, 5 Nov 2022 18:31:49 -0500 Subject: [PATCH] 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. --- .../maintenance/user/deletions_controller.rb | 26 ------- app/controllers/users_controller.rb | 26 +++++++ app/policies/user_policy.rb | 8 ++ .../maintenance/user/deletions/show.html.erb | 40 ---------- app/views/static/privacy_policy.html.erb | 4 +- app/views/users/deactivate.html.erb | 47 ++++++++++++ app/views/users/edit.html.erb | 2 +- config/routes.rb | 4 +- .../user/deletions_controller_test.rb | 42 ---------- test/functional/users_controller_test.rb | 76 +++++++++++++++++++ 10 files changed, 163 insertions(+), 112 deletions(-) delete mode 100644 app/controllers/maintenance/user/deletions_controller.rb delete mode 100644 app/views/maintenance/user/deletions/show.html.erb create mode 100644 app/views/users/deactivate.html.erb delete mode 100644 test/functional/maintenance/user/deletions_controller_test.rb diff --git a/app/controllers/maintenance/user/deletions_controller.rb b/app/controllers/maintenance/user/deletions_controller.rb deleted file mode 100644 index f029066b6..000000000 --- a/app/controllers/maintenance/user/deletions_controller.rb +++ /dev/null @@ -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 diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9dc0639f9..6ab1a14c4 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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 diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 9ad9f1fb1..e8145fbf1 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -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 diff --git a/app/views/maintenance/user/deletions/show.html.erb b/app/views/maintenance/user/deletions/show.html.erb deleted file mode 100644 index 7334839bb..000000000 --- a/app/views/maintenance/user/deletions/show.html.erb +++ /dev/null @@ -1,40 +0,0 @@ -<% page_title "Deactivate Account" %> - -
-
-

Deactivate Account

- -

- You can deactivate your <%= Danbooru.config.app_name %> account using - the form below. Deactivating your account will do the following things: -

- -
    -
  • Change your name to a generic name
  • -
  • Delete your favorites
  • -
  • Delete your saved searches
  • -
  • Delete your password and email address
  • -
  • Delete your account settings
  • -
- -

- The following things will not be deleted: -

- -
    -
  • Posts you've uploaded
  • -
  • Comments, forum posts, and private messages
  • -
  • Tag edits, wiki edits, translation notes, or any other contributions to the site
  • -
- -

- Enter your password below to deactivate your account. This cannot be - undone. Your account cannot be recovered after it is deactivated. -

- - <%= 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 %> -
-
diff --git a/app/views/static/privacy_policy.html.erb b/app/views/static/privacy_policy.html.erb index 6f1e7da32..c62736662 100644 --- a/app/views/static/privacy_policy.html.erb +++ b/app/views/static/privacy_policy.html.erb @@ -353,8 +353,8 @@

Summary: You may deactivate your account, but we may retain your IP address and your public contributions to the Site.

-

If you would like to delete your account, you may request your - account to be deactivated <%= link_to "here", maintenance_user_deletion_path %>.

+

If you wish to to close your <%= @app_name %> account, you may deactivate your account + <%= link_to "here", deactivate_users_path %>.

When your account is deactivated, we will delete your non-public personal information from our active systems, including your email address, diff --git a/app/views/users/deactivate.html.erb b/app/views/users/deactivate.html.erb new file mode 100644 index 000000000..3528325ef --- /dev/null +++ b/app/views/users/deactivate.html.erb @@ -0,0 +1,47 @@ +<% page_title "Deactivate Account" %> +<%= render "secondary_links" %> + +

+
+ <% if @user == CurrentUser.user %> +

Deactivate Account

+ <% else %> +

Deactivate Account: <%= link_to_user @user %>

+ <% end %> + +
+

+ You can deactivate your <%= Danbooru.config.app_name %> account by entering your password below. Deactivating + your account will do the following things: +

+ +
    +
  • Change your username to a generic username (user_<%= @user.id %>).
  • +
  • Delete your password, email address, and account settings.
  • +
  • Delete your favorites.
  • +
  • Delete your saved searches.
  • +
+ +

+ The following things will not be deleted: +

+ +
    +
  • Posts you've uploaded.
  • +
  • Your comments, forum posts, and private messages.
  • +
  • Your tag edits, wiki edits, translation notes, and any other contributions you've made to the site.
  • +
  • Your login history, including your IP address and geographic location. This is kept for moderation purposes.
  • +
+ +

+ Enter your password below to deactivate your account. This cannot be + undone. Your account cannot be recovered after it is deactivated. +

+
+ + <%= 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 %> +
+
diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 33eb21260..875c4b7ab 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -53,7 +53,7 @@

- <%= link_to "Deactivate account", maintenance_user_deletion_path, id: "delete-account" %> + <%= link_to "Deactivate account", deactivate_user_path(@user), id: "delete-account" %> (requires confirmation)

diff --git a/config/routes.rb b/config/routes.rb index 8ffb5d8e3..f85bc97be 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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" diff --git a/test/functional/maintenance/user/deletions_controller_test.rb b/test/functional/maintenance/user/deletions_controller_test.rb deleted file mode 100644 index 07fab37c3..000000000 --- a/test/functional/maintenance/user/deletions_controller_test.rb +++ /dev/null @@ -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 diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 05238427f..df3a3df97 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -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; }")