Merge LDAP improvements branch into develop.

This commit is contained in:
Thorsten Eckel 2017-04-28 10:10:05 +02:00
commit d49d257ffa
3 changed files with 82 additions and 12 deletions

View file

@ -6,7 +6,10 @@ module Import
def initialize(resource, *args) def initialize(resource, *args)
handle_args(resource, *args) handle_args(resource, *args)
initialize_associations_states
import(resource, *args) import(resource, *args)
return if @resource.blank?
store_associations(:after, @resource)
end end
def import_class def import_class
@ -30,8 +33,7 @@ module Import
end end
def attributes_changed? def attributes_changed?
return true if changed_attributes.present? changed_attributes.present? || changed_associations.present?
@associations_init != associations_state(@resource)
end end
def changed_attributes def changed_attributes
@ -42,6 +44,15 @@ module Import
@resource.previous_changes @resource.previous_changes
end end
def changed_associations
changes = {}
tracked_associations.each do |association|
next if @associations[:before][association] == @associations[:after][association]
changes[association] = [@associations[:before][association], @associations[:after][association]]
end
changes
end
def created? def created?
return false if @resource.blank? return false if @resource.blank?
# dry run # dry run
@ -52,6 +63,13 @@ module Import
private private
def initialize_associations_states
@associations = {}
%i(before after).each do |state|
@associations[state] ||= {}
end
end
def import(resource, *args) def import(resource, *args)
create_or_update(map(resource, *args), *args) create_or_update(map(resource, *args), *args)
rescue => e rescue => e
@ -96,13 +114,13 @@ module Import
return if !synced_instance return if !synced_instance
instance = import_class.find_by(id: synced_instance.o_id) instance = import_class.find_by(id: synced_instance.o_id)
store_associations_state(instance) store_associations(:before, instance)
instance instance
end end
def store_associations_state(instance) def store_associations(state, instance)
@associations_init = associations_state(instance) @associations[state] = associations_state(instance)
end end
def associations_state(instance) def associations_state(instance)

View file

@ -31,7 +31,6 @@ module Import
def create_or_update(resource, *args) def create_or_update(resource, *args)
return if skip?(resource) return if skip?(resource)
resource[:role_ids] = role_ids(resource)
result = super(resource, *args) result = super(resource, *args)
ldap_log( ldap_log(
@ -58,14 +57,49 @@ module Import
return @signup_role_ids if !dn return @signup_role_ids if !dn
# check if roles are mapped for the found dn # check if roles are mapped for the found dn
roles = @dn_roles[ dn.downcase ] roles = @dn_roles[ dn.downcase ]
# use signup roles if no mapped roles were found
return @signup_role_ids if !roles
# return found roles # return found roles
roles return roles if roles
# return signup roles if there is a role mapping in general
# this makes the LDAP the leading source of roles
return @signup_role_ids if @dn_roles.present?
# special case: there is no LDAP role mapping configured
#
# if there is no role mapping in general the signup roles
# should only get used for newly created users (see .create method)
# otherwise users might overwrite their local assigned
# roles with less privileged roles and lock theirselfs out
#
# see issue 350#issuecomment-295252402
#
# this method only gets called from the .updated? method
# so we won't return any roles which will keep the current
# role assignment for the User
# the signup roles will be taken in this case direcly in
# the .create method
nil
end end
def updated?(resource, *_args) def updated?(resource, *_args)
super
# this is needed for the special case described in the role_ids method
#
# assign roles only if there are any found in the mapping
# otherwise keep those stored to the User
update_roles = role_ids(resource)
resource[:role_ids] = update_roles if update_roles
user_found = super
# in case a User was found and we had no roles
# to set/update we have to note the currently
# assigned roles so that our action check won't
# falsly detect changes to the User
if user_found && update_roles.blank?
resource[:role_ids] = @resource.role_ids
end
user_found
rescue => e rescue => e
ldap_log( ldap_log(
action: "update -> #{resource[:login]}", action: "update -> #{resource[:login]}",
@ -94,7 +128,7 @@ module Import
remote: resource, remote: resource,
) )
store_associations_state(instance) store_associations(:before, instance)
end end
instance instance
@ -122,6 +156,11 @@ module Import
end end
def create(resource, *_args) def create(resource, *_args)
# this is needed for the special case described in the role_ids method
#
# in case we have no role IDs yet we have to fall back to the signup roles
resource[:role_ids] ||= @signup_role_ids
super super
rescue => e rescue => e
ldap_log( ldap_log(

View file

@ -99,7 +99,12 @@ RSpec.describe Import::Ldap::User do
context 'update' do context 'update' do
before(:each) do before(:each) do
user = create(:user, login: uid) user = create(:user,
login: uid,
role_ids: [
Role.find_by(name: 'Agent').id,
Role.find_by(name: 'Admin').id
])
ExternalSync.create( ExternalSync.create(
source: 'Ldap::User', source: 'Ldap::User',
@ -119,6 +124,14 @@ RSpec.describe Import::Ldap::User do
} }
end end
it "doesn't change roles if no role mapping is configured" do
expect do
described_class.new(user_entry, ldap_config, {}, signup_role_ids)
end.to not_change {
User.last.role_ids
}
end
it 'creates an HTTP Log entry' do it 'creates an HTTP Log entry' do
expect do expect do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids) described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)