move account deletion out of a tranaction and into a job
This commit is contained in:
parent
7204ef8e26
commit
686464c36e
9 changed files with 152 additions and 74 deletions
|
|
@ -71,7 +71,8 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
current_user.destroy
|
Resque.enqueue(Job::DeleteAccount, current_user.id)
|
||||||
|
current_user.lock_access!
|
||||||
sign_out current_user
|
sign_out current_user
|
||||||
flash[:notice] = I18n.t 'users.destroy'
|
flash[:notice] = I18n.t 'users.destroy'
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
|
|
|
||||||
15
app/models/jobs/delete_account.rb
Normal file
15
app/models/jobs/delete_account.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2010, Diaspora Inc. This file is
|
||||||
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
|
|
||||||
|
module Job
|
||||||
|
class DeleteAccount < Base
|
||||||
|
@queue = :delete_account
|
||||||
|
def self.perform_delegate(user_id)
|
||||||
|
user = User.find(user_id)
|
||||||
|
user.remove_all_traces
|
||||||
|
user.destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -13,7 +13,8 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
devise :invitable, :database_authenticatable, :registerable,
|
devise :invitable, :database_authenticatable, :registerable,
|
||||||
:recoverable, :rememberable, :trackable, :validatable,
|
:recoverable, :rememberable, :trackable, :validatable,
|
||||||
:timeoutable, :token_authenticatable
|
:timeoutable, :token_authenticatable, :lockable,
|
||||||
|
:lock_strategy => :none, :unlock_strategy => :none
|
||||||
|
|
||||||
before_validation :strip_and_downcase_username
|
before_validation :strip_and_downcase_username
|
||||||
before_validation :set_current_language, :on => :create
|
before_validation :set_current_language, :on => :create
|
||||||
|
|
@ -39,11 +40,11 @@ class User < ActiveRecord::Base
|
||||||
has_many :services
|
has_many :services
|
||||||
has_many :user_preferences
|
has_many :user_preferences
|
||||||
|
|
||||||
before_destroy :disconnect_everyone, :remove_mentions, :remove_person
|
|
||||||
before_save do
|
before_save do
|
||||||
person.save if person && person.changed?
|
person.save if person && person.changed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail
|
attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail
|
||||||
|
|
||||||
def update_user_preferences(pref_hash)
|
def update_user_preferences(pref_hash)
|
||||||
|
|
@ -317,7 +318,11 @@ class User < ActiveRecord::Base
|
||||||
AppConfig[:admins].present? && AppConfig[:admins].include?(self.username)
|
AppConfig[:admins].present? && AppConfig[:admins].include?(self.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
def remove_all_traces
|
||||||
|
disconnect_everyone
|
||||||
|
remove_mentions
|
||||||
|
remove_person
|
||||||
|
end
|
||||||
|
|
||||||
def remove_person
|
def remove_person
|
||||||
self.person.destroy
|
self.person.destroy
|
||||||
|
|
@ -325,11 +330,11 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
def disconnect_everyone
|
def disconnect_everyone
|
||||||
self.contacts.each do |contact|
|
self.contacts.each do |contact|
|
||||||
unless contact.person.owner.nil?
|
if contact.person.remote?
|
||||||
|
self.disconnect(contact)
|
||||||
|
else
|
||||||
contact.person.owner.disconnected_by(self.person)
|
contact.person.owner.disconnected_by(self.person)
|
||||||
remove_contact(contact, :force => true)
|
remove_contact(contact, :force => true)
|
||||||
else
|
|
||||||
self.disconnect(contact)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.aspects.destroy_all
|
self.aspects.destroy_all
|
||||||
|
|
|
||||||
|
|
@ -704,7 +704,7 @@ en:
|
||||||
private_message: "...you receive a private message?"
|
private_message: "...you receive a private message?"
|
||||||
liked: "...someone likes your post?"
|
liked: "...someone likes your post?"
|
||||||
change: "Change"
|
change: "Change"
|
||||||
destroy: "Account successfully closed."
|
destroy: "Your account has been locked. It may take 20 minutes for us to finish closing your account. Thank you for trying Diaspora."
|
||||||
getting_started:
|
getting_started:
|
||||||
welcome: "Welcome to Diaspora!"
|
welcome: "Welcome to Diaspora!"
|
||||||
signup_steps: "Finish your sign up by completing these three steps:"
|
signup_steps: "Finish your sign up by completing these three steps:"
|
||||||
|
|
|
||||||
9
db/migrate/20110603181015_lockable_users.rb
Normal file
9
db/migrate/20110603181015_lockable_users.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
class LockableUsers < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
add_column :users, :locked_at, :datetime
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_column :users, :locked_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 20110527135552) do
|
ActiveRecord::Schema.define(:version => 20110603181015) do
|
||||||
|
|
||||||
create_table "aspect_memberships", :force => true do |t|
|
create_table "aspect_memberships", :force => true do |t|
|
||||||
t.integer "aspect_id", :null => false
|
t.integer "aspect_id", :null => false
|
||||||
|
|
@ -369,6 +369,7 @@ ActiveRecord::Schema.define(:version => 20110527135552) do
|
||||||
t.integer "invited_by_id"
|
t.integer "invited_by_id"
|
||||||
t.string "invited_by_type"
|
t.string "invited_by_type"
|
||||||
t.string "authentication_token", :limit => 30
|
t.string "authentication_token", :limit => 30
|
||||||
|
t.datetime "locked_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
|
add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
|
||||||
|
|
|
||||||
|
|
@ -145,4 +145,15 @@ describe UsersController do
|
||||||
response.should redirect_to new_user_session_path
|
response.should redirect_to new_user_session_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#destroy' do
|
||||||
|
it 'enqueues a delete job' do
|
||||||
|
Resque.should_receive(:enqueue).with(Job::DeleteAccount, alice.id)
|
||||||
|
delete :destroy
|
||||||
|
end
|
||||||
|
it 'locks the user out' do
|
||||||
|
delete :destroy
|
||||||
|
alice.reload.access_locked?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
30
spec/models/jobs/delete_account_spec.rb
Normal file
30
spec/models/jobs/delete_account_spec.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Copyright (c) 2011, Diaspora Inc. This file is
|
||||||
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Job::DeleteAccount do
|
||||||
|
describe '#perform' do
|
||||||
|
it 'calls remove_all_traces' do
|
||||||
|
stub_find_for(bob)
|
||||||
|
bob.should_receive(:remove_all_traces)
|
||||||
|
Job::DeleteAccount.perform(bob.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls destroy' do
|
||||||
|
stub_find_for(bob)
|
||||||
|
bob.should_receive(:destroy)
|
||||||
|
Job::DeleteAccount.perform(bob.id)
|
||||||
|
end
|
||||||
|
def stub_find_for model
|
||||||
|
model.class.stub!(:find) do |id, conditions|
|
||||||
|
if id == model.id
|
||||||
|
model
|
||||||
|
else
|
||||||
|
model.class.find_by_id(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -374,71 +374,68 @@ describe User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'account removal' do
|
describe 'account deletion' do
|
||||||
it 'should disconnect everyone' do
|
describe '#remove_all_traces' do
|
||||||
alice.should_receive(:disconnect_everyone)
|
it 'should disconnect everyone' do
|
||||||
alice.destroy
|
alice.should_receive(:disconnect_everyone)
|
||||||
|
alice.remove_all_traces
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it 'should remove mentions' do
|
||||||
|
alice.should_receive(:remove_mentions)
|
||||||
|
alice.remove_all_traces
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should remove person' do
|
||||||
|
alice.should_receive(:remove_person)
|
||||||
|
alice.remove_all_traces
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should remove all aspects' do
|
||||||
|
lambda {
|
||||||
|
alice.remove_all_traces
|
||||||
|
}.should change{ alice.aspects(true).count }.by(-1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes invitations from the user' do
|
describe '#destroy' do
|
||||||
alice.invite_user alice.aspects.first.id, 'email', 'blah@blah.blah'
|
it 'removes invitations from the user' do
|
||||||
lambda {
|
alice.invite_user alice.aspects.first.id, 'email', 'blah@blah.blah'
|
||||||
alice.destroy
|
lambda {
|
||||||
}.should change {alice.invitations_from_me(true).count }.by(-1)
|
alice.destroy
|
||||||
end
|
}.should change {alice.invitations_from_me(true).count }.by(-1)
|
||||||
|
end
|
||||||
|
|
||||||
it 'removes invitations to the user' do
|
it 'removes invitations to the user' do
|
||||||
Invitation.create(:sender_id => eve.id, :recipient_id => alice.id, :aspect_id => eve.aspects.first.id)
|
Invitation.create(:sender_id => eve.id, :recipient_id => alice.id, :aspect_id => eve.aspects.first.id)
|
||||||
lambda {
|
lambda {
|
||||||
alice.destroy
|
alice.destroy
|
||||||
}.should change {alice.invitations_to_me(true).count }.by(-1)
|
}.should change {alice.invitations_to_me(true).count }.by(-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should remove mentions' do
|
it 'removes all service connections' do
|
||||||
alice.should_receive(:remove_mentions)
|
Services::Facebook.create(:access_token => 'what', :user_id => alice.id)
|
||||||
alice.destroy
|
lambda {
|
||||||
end
|
alice.destroy
|
||||||
|
}.should change {
|
||||||
it 'should remove person' do
|
alice.services.count
|
||||||
alice.should_receive(:remove_person)
|
}.by(-1)
|
||||||
alice.destroy
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it 'should remove all aspects' do
|
|
||||||
lambda {
|
|
||||||
alice.destroy
|
|
||||||
}.should change{ alice.aspects(true).count }.by(-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'removes all contacts' do
|
|
||||||
lambda {
|
|
||||||
alice.destroy
|
|
||||||
}.should change {
|
|
||||||
alice.contacts.count
|
|
||||||
}.by(-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'removes all service connections' do
|
|
||||||
Services::Facebook.create(:access_token => 'what', :user_id => alice.id)
|
|
||||||
lambda {
|
|
||||||
alice.destroy
|
|
||||||
}.should change {
|
|
||||||
alice.services.count
|
|
||||||
}.by(-1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#remove_person' do
|
describe '#remove_person' do
|
||||||
it 'should remove the person object' do
|
it 'should remove the person object' do
|
||||||
person = alice.person
|
person = alice.person
|
||||||
alice.destroy
|
alice.remove_person
|
||||||
person.reload
|
person.reload
|
||||||
person.should be nil
|
person.should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should remove the posts' do
|
it 'should remove the posts' do
|
||||||
message = alice.post(:status_message, :text => "hi", :to => alice.aspects.first.id)
|
message = alice.post(:status_message, :text => "hi", :to => alice.aspects.first.id)
|
||||||
alice.reload
|
alice.reload
|
||||||
alice.destroy
|
alice.remove_person
|
||||||
proc { message.reload }.should raise_error ActiveRecord::RecordNotFound
|
proc { message.reload }.should raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -449,41 +446,50 @@ describe User do
|
||||||
sm = Factory(:status_message)
|
sm = Factory(:status_message)
|
||||||
mention = Mention.create(:person => person, :post=> sm)
|
mention = Mention.create(:person => person, :post=> sm)
|
||||||
alice.reload
|
alice.reload
|
||||||
alice.destroy
|
alice.remove_mentions
|
||||||
proc { mention.reload }.should raise_error ActiveRecord::RecordNotFound
|
proc { mention.reload }.should raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#disconnect_everyone' do
|
describe '#disconnect_everyone' do
|
||||||
it 'has no error on a local friend who has deleted his account' do
|
it 'has no error on a local friend who has deleted his account' do
|
||||||
alice.destroy
|
Job::DeleteAccount.perform(alice.id)
|
||||||
lambda {
|
lambda {
|
||||||
bob.destroy
|
bob.disconnect_everyone
|
||||||
}.should_not raise_error
|
}.should_not raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has no error when the user has sent local requests' do
|
it 'has no error when the user has sent local requests' do
|
||||||
alice.share_with(eve.person, alice.aspects.first)
|
alice.share_with(eve.person, alice.aspects.first)
|
||||||
lambda {
|
lambda {
|
||||||
alice.destroy
|
alice.disconnect_everyone
|
||||||
}.should_not raise_error
|
}.should_not raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should send retractions to remote poeple' do
|
it 'sends retractions to remote poeple' do
|
||||||
person = eve.person
|
person = eve.person
|
||||||
eve.delete
|
eve.delete
|
||||||
|
person.owner_id = nil
|
||||||
person.save
|
person.save
|
||||||
alice.contacts.create(:person => person, :aspects => [alice.aspects.first])
|
alice.contacts.create(:person => person, :aspects => [alice.aspects.first])
|
||||||
|
|
||||||
alice.should_receive(:disconnect).once
|
alice.should_receive(:disconnect).once
|
||||||
alice.destroy
|
alice.disconnect_everyone
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should disconnect local people' do
|
it 'disconnects local people' do
|
||||||
lambda {
|
lambda {
|
||||||
alice.destroy
|
alice.remove_all_traces
|
||||||
}.should change{bob.reload.contacts.count}.by(-1)
|
}.should change{bob.reload.contacts.count}.by(-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'removes all contacts' do
|
||||||
|
lambda {
|
||||||
|
alice.disconnect_everyone
|
||||||
|
}.should change {
|
||||||
|
alice.contacts.count
|
||||||
|
}.by(-1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue