Fixed bug #1070: Existing resources get "save!"ed without changes. This triggers Rails model callbacks and executes unneeded actions.

This was caused by an error in the detection of changed associations. Unchanged associations were interpreted as a reset of the association. Therefore all associations without a new value won't get tracked anymore. This is the right behavior since they won't change the current value of the resource without a value.
This commit is contained in:
Thorsten Eckel 2017-05-12 17:24:15 +02:00
parent 199686dce1
commit 22f87c8826
3 changed files with 38 additions and 42 deletions

View file

@ -45,8 +45,13 @@ module Import
def changed_associations def changed_associations
changes = {} changes = {}
tracked_associations.each do |association| tracked_associations.each do |association|
# skip if no new value will get assigned (no change is performed)
next if !@associations[:after].key?(association)
# skip if both values are equal
next if @associations[:before][association] == @associations[:after][association] next if @associations[:before][association] == @associations[:after][association]
# skip if both values are blank
next if @associations[:before][association].blank? && @associations[:after][association].blank? next if @associations[:before][association].blank? && @associations[:after][association].blank?
# store changes
changes[association] = [@associations[:before][association], @associations[:after][association]] changes[association] = [@associations[:before][association], @associations[:after][association]]
end end
changes changes
@ -127,19 +132,22 @@ module Import
def associations_state(source) def associations_state(source)
state = {} state = {}
tracked_associations.each do |association| tracked_associations.each do |association|
state[association] = association_value(source, association) # we have to support instances and (resource) hashes
# here since in case of an update we only have the
# hash as a source but on create we have an instance
if source.is_a?(Hash)
# ignore if there is no key for the association
# of the Hash (update)
# otherwise wrong changes may get detected
next if !source.key?(association)
state[association] = source[association]
else
state[association] = source.send(association)
end
end end
state state
end end
# we have to support instances and (resource) hashes
# here since in case of an update we only have the
# hash as a source but on create we have an instance
def association_value(source, association)
return source[association] if source.is_a?(Hash)
source.send(association)
end
def tracked_associations def tracked_associations
# loop over all reflections # loop over all reflections
import_class.reflect_on_all_associations.collect do |reflection| import_class.reflect_on_all_associations.collect do |reflection|

View file

@ -100,19 +100,6 @@ module Import
user_found = super user_found = super
end end
# in case an User was found and we had no roles
# to set/update we have to note the currently locally
# assigned roles so that our action check won't
# falsly detect changes to the User (from nil to current)
if user_found && @update_role_ids.blank?
resource[:role_ids] = @resource.role_ids
# we have to re-store/overwrite the stored
# associations since we've added the current
# state of the roles just yet
store_associations(:after, resource)
end
user_found user_found
rescue => e rescue => e
ldap_log( ldap_log(

View file

@ -220,24 +220,6 @@ RSpec.describe Import::Ldap::User do
} }
end end
it 'keeps local roles if signup roles are configured' do
ldap_config[:unassigned_users] = 'sigup_roles'
expect do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
end.not_to change {
User.last.role_ids
}
end
it "doesn't detect false changes if signup roles are configured" do
# make sure that the nothing has changed
User.find_by(login: uid).update_attribute(:email, 'example@example.com')
instance = described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(instance.action).to eq(:unchanged)
end
it 'skips user if configured' do it 'skips user if configured' do
ldap_config[:unassigned_users] = 'skip_sync' ldap_config[:unassigned_users] = 'skip_sync'
@ -250,6 +232,27 @@ RSpec.describe Import::Ldap::User do
} }
expect(instance.action).to eq(:skipped) expect(instance.action).to eq(:skipped)
end end
context 'signup roles configuration' do
it 'keeps local roles' do
ldap_config[:unassigned_users] = 'sigup_roles'
expect do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
end.not_to change {
User.last.role_ids
}
end
it "doesn't detect false changes" do
# make sure that the nothing has changed
User.find_by(login: uid).update_attribute(:email, 'example@example.com')
expect_any_instance_of(User).not_to receive(:save!)
instance = described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(instance.action).to eq(:unchanged)
end
end
end end
end end
@ -265,7 +268,6 @@ RSpec.describe Import::Ldap::User do
User.count User.count
} }
expect(instance.action).to eq(:skipped) expect(instance.action).to eq(:skipped)
end end
it 'skips entries without attributes' do it 'skips entries without attributes' do
@ -288,6 +290,5 @@ RSpec.describe Import::Ldap::User do
expect(HttpLog.last.status).to eq('success') expect(HttpLog.last.status).to eq('success')
expect(HttpLog.last.url).to start_with('skipped') expect(HttpLog.last.url).to start_with('skipped')
end end
end end
end end