diff --git a/app/controllers/maintenance/user/deletions_controller.rb b/app/controllers/maintenance/user/deletions_controller.rb index 664062b88..2022595b2 100644 --- a/app/controllers/maintenance/user/deletions_controller.rb +++ b/app/controllers/maintenance/user/deletions_controller.rb @@ -1,10 +1,18 @@ module Maintenance module User class DeletionsController < ApplicationController + rescue_from UserDeletion::ValidationError, :with => :rescue_exception + def show end def destroy + deletion = UserDeletion.new(CurrentUser.user, params[:password]) + deletion.delete! + session.delete(:user_id) + cookies.delete(:cookie_password_hash) + cookies.delete(:user_name) + redirect_to(posts_path, :notice => "You are now logged out") end end end diff --git a/app/logical/user_deletion.rb b/app/logical/user_deletion.rb new file mode 100644 index 000000000..459e6dc9f --- /dev/null +++ b/app/logical/user_deletion.rb @@ -0,0 +1,71 @@ +class UserDeletion + class ValidationError < Exception ; end + + attr_reader :user, :password + + def initialize(user, password) + @user = user + @password = password + end + + def delete! + validate + clear_user_settings + remove_favorites + rename + reset_password + end + +private + + def clear_user_settings + user.email = nil + user.last_logged_in_at = nil + user.last_forum_read_at = nil + user.recent_tags = nil + user.favorite_tags = nil + user.blacklisted_tags = nil + user.hide_deleted_posts = false + user.time_zone = "Eastern Time (US & Canada)" + user.save! + end + + def reset_password + random = SecureRandom.hex(16) + user.password = random + user.password_confirmation = random + user.old_password = password + user.save! + end + + def remove_favorites + Post.tag_match("fav:#{user.name}").find_each do |post| + Favorite.remove(post, user) + end + end + + def rename + name = "user_#{user.id}" + n = 0 + while User.where(:name => name).exists? && (n < 10) + name += "~" + end + + if n == 10 + raise ValidationError.new("New name could not be found") + end + + user.name = name + user.save! + end + + def validate + if !User.authenticate(user.name, password) + raise ValidationError.new("Password is incorrect") + end + + if user.level >= User::Levels::ADMIN + raise ValidationError.new("Admins cannot delete their account") + end + end +end diff --git a/app/views/maintenance/user/deletions/show.html.erb b/app/views/maintenance/user/deletions/show.html.erb index 916a622d3..217292c8e 100644 --- a/app/views/maintenance/user/deletions/show.html.erb +++ b/app/views/maintenance/user/deletions/show.html.erb @@ -6,7 +6,18 @@
  • Rename your account to a generic string
  • Scramble your password
  • Remove all your favorites
  • +
  • Blank out your settings (including email)
  • You must enter your password to delete your account.

    +<%= form_tag(maintenance_user_deletion_path, :method => :delete, :class => "simple_form") do %> +
    + + <%= password_field_tag :password %> +
    + +
    + <%= submit_tag %> +
    +<% end %> \ No newline at end of file diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index c25b2806d..3575ccc02 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -25,8 +25,6 @@ <%= f.input :per_page, :label => "Posts per page", :as => :select, :collection => (1..100), :include_blank => false %> <% end %> - <%= f.input :style_usernames, :as => :select, :label => "Colored usernames", :hint => "Color each user's name depending on their rank", :include_blank => false, :collection => [["Yes", "true"], ["No", "false"]] %> - <%= f.input :blacklisted_tags, :hint => "Put any tag combinations you never want to see here. Each combination should go on a separate line.", :input_html => {:size => "40x5"} %>
    @@ -37,6 +35,7 @@
    Advanced Settings + <%= f.input :style_usernames, :as => :select, :label => "Colored usernames", :hint => "Color each user's name depending on their rank", :include_blank => false, :collection => [["Yes", "true"], ["No", "false"]] %> <%= f.input :always_resize_images, :as => :select, :include_blank => false, :label => "Fit images to window", :hint => "Use JavaScript to resize images to fit window" %> <%= f.input :enable_post_navigation, :as => :select, :include_blank => false, :label => "Enable keyboard shortcuts" %> <%= f.input :new_post_navigation_layout, :as => :select, :label => "Pool links", :include_blank => false, :collection => [["Bottom", "true"], ["Top", "false"]], :hint => "When browsing pools, where do you want the navigation links to be placed?" %> @@ -50,6 +49,11 @@ <%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %>
    +
    + Delete Account +

    <%= link_to "Click here to delete your account", maintenance_user_deletion_path %>

    +
    + <%= f.button :submit, "Submit" %> <% end %>
    diff --git a/test/functional/maintenance/user/deletions_controller_test.rb b/test/functional/maintenance/user/deletions_controller_test.rb new file mode 100644 index 000000000..e092a268b --- /dev/null +++ b/test/functional/maintenance/user/deletions_controller_test.rb @@ -0,0 +1,29 @@ +require "test_helper" + +module Maintenance + module User + class DeletionsControllerTest < ActionController::TestCase + context "in all cases" do + setup do + @user = FactoryGirl.create(:user) + CurrentUser.user = @user + CurrentUser.ip_addr = "127.0.0.1" + end + + context "#show" do + should "render" do + get :show, {}, {:user_id => @user.id} + assert_response :success + end + end + + context "#destroy" do + should "render" do + post :destroy, {:password => "password"}, {:user_id => @user.id} + assert_redirected_to(posts_path) + end + end + end + end + end +end \ No newline at end of file diff --git a/test/unit/user_deletion_test.rb b/test/unit/user_deletion_test.rb new file mode 100644 index 000000000..3fd9b2bda --- /dev/null +++ b/test/unit/user_deletion_test.rb @@ -0,0 +1,71 @@ +require 'test_helper' + +class UserDeletionTest < ActiveSupport::TestCase + context "an invalid user deletion" do + context "for an invalid password" do + setup do + @user = FactoryGirl.create(:user) + CurrentUser.user = @user + CurrentUser.ip_addr = "127.0.0.1" + @deletion = UserDeletion.new(@user, "wrongpassword") + end + + should "fail" do + assert_raise(UserDeletion::ValidationError) do + @deletion.delete! + end + end + end + + context "for an admin" do + setup do + @user = FactoryGirl.create(:admin_user) + CurrentUser.user = @user + CurrentUser.ip_addr = "127.0.0.1" + @deletion = UserDeletion.new(@user, "password") + end + + should "fail" do + assert_raise(UserDeletion::ValidationError) do + @deletion.delete! + end + end + end + end + + context "a valid user deletion" do + setup do + @user = FactoryGirl.create(:user) + CurrentUser.user = @user + CurrentUser.ip_addr = "127.0.0.1" + + @post = FactoryGirl.create(:post) + Favorite.add(@post, @user) + + @user.update_attributes(:email => "ted@danbooru.com") + + @deletion = UserDeletion.new(@user, "password") + @deletion.delete! + @user.reload + end + + should "blank out the email" do + assert_nil(@user.email) + end + + should "rename the user" do + assert_equal("user_#{@user.id}", @user.name) + end + + should "reset the password" do + assert_nil(User.authenticate(@user.name, "password")) + end + + should "remove any favorites" do + @post.reload + assert_equal(0, Favorite.count) + assert_equal("", @post.fav_string) + assert_equal(0, @post.fav_count) + end + end +end