Working on issue #981 - Make user filter and role assignments default behaviour configurable.

This commit is contained in:
Thorsten Eckel 2017-05-10 09:50:48 +02:00
parent 1414f3c7e4
commit 65443b3768
5 changed files with 199 additions and 78 deletions

View file

@ -158,6 +158,7 @@ class ConnectionWizard extends App.WizardModal
'.modal-body': 'body' '.modal-body': 'body'
'.js-userMappingForm': 'userMappingForm' '.js-userMappingForm': 'userMappingForm'
'.js-groupRoleForm': 'groupRoleForm' '.js-groupRoleForm': 'groupRoleForm'
'.js-expertForm': 'expertForm'
constructor: -> constructor: ->
super super
@ -368,6 +369,14 @@ class ConnectionWizard extends App.WizardModal
@groupRoleForm.find('tbody tr.js-entry').remove() @groupRoleForm.find('tbody tr.js-entry').remove()
@groupRoleForm.find('tbody tr').before(@buildRowsGroupRole(@wizardConfig.group_role_map)) @groupRoleForm.find('tbody tr').before(@buildRowsGroupRole(@wizardConfig.group_role_map))
@$('.js-mapping input[name="user_filter"]').val(@wizardConfig.user_filter)
unassigned_users_choices =
sigup_roles: App.i18n.translatePlain('Assign signup roles')
skip_sync: App.i18n.translatePlain('Don\'t synchronize')
@$('.js-unassignedUsers').html(@createSelection('unassigned_users', unassigned_users_choices, @wizardConfig.unassigned_users || 'sigup_roles'))
mappingChange: (e) => mappingChange: (e) =>
e.preventDefault() e.preventDefault()
@ -396,6 +405,11 @@ class ConnectionWizard extends App.WizardModal
group_role_map_local[group_role_map.source[count]] = group_role_map.dest[count] group_role_map_local[group_role_map.source[count]] = group_role_map.dest[count]
@wizardConfig.group_role_map = group_role_map_local @wizardConfig.group_role_map = group_role_map_local
expertSettings = @formParam(@expertForm)
@wizardConfig.user_filter = expertSettings.user_filter
@wizardConfig.unassigned_users = expertSettings.unassigned_users
@tryShow() @tryShow()
buildRowsUserMap: (user_attribute_map) => buildRowsUserMap: (user_attribute_map) =>

View file

@ -169,7 +169,6 @@
</form> </form>
<h2><%- @T('Roles') %></h2> <h2><%- @T('Roles') %></h2>
<p><%- @T('Note: All not mapped users will get the default signup roles.') %></p>
<form class="js-groupRoleForm"> <form class="js-groupRoleForm">
<table class="settings-list js-groupRoleMap" style="width: 100%;"> <table class="settings-list js-groupRoleMap" style="width: 100%;">
<colgroup> <colgroup>
@ -193,6 +192,25 @@
</table> </table>
</form> </form>
<h2><%- @T('Expert') %></h2>
<form class="js-expertForm">
<table class="settings-list js-expert" style="width: 100%;">
<thead>
<tr>
<th width="30%"><%- @T('Name') %>
<th width="70%"><%- @T('Value') %>
</thead>
<tbody>
<tr>
<td class="settings-list-row-control"><%- @T('User filter') %>
<td class="settings-list-control-cell"><input type="text" name="user_filter" class="form-control form-control--small" value="" placeholder="" autocomplete="new-password">
<tr>
<td class="settings-list-row-control"><%- @T('Users without assigned LDAP groups') %>
<td class="settings-list-control-cell js-unassignedUsers">
</tbody>
</table>
</form>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View file

@ -12,7 +12,7 @@ module Import
normalized_entry = normalize_entry(resource) normalized_entry = normalize_entry(resource)
# extract the uid attribute and store it as # extract the uid attribute and store it as
# the remote ID so we can access later # the remote ID so we can access it later
# when working with ExternalSync # when working with ExternalSync
@remote_id = normalized_entry[ @ldap_config[:user_uid].to_sym ] @remote_id = normalized_entry[ @ldap_config[:user_uid].to_sym ]
@ -31,6 +31,11 @@ module Import
def create_or_update(resource, *args) def create_or_update(resource, *args)
return if skip?(resource) return if skip?(resource)
result = nil
catch(:no_roles_assigned) do
determine_role_ids(resource)
result = super(resource, *args) result = super(resource, *args)
ldap_log( ldap_log(
@ -38,6 +43,7 @@ module Import
status: 'success', status: 'success',
request: resource, request: resource,
) )
end
result result
end end
@ -50,56 +56,55 @@ module Import
!resource.except(*ignored_attributes).values.any?(&:present?) !resource.except(*ignored_attributes).values.any?(&:present?)
end end
def role_ids(resource) def determine_role_ids(resource)
# remove remporary added and get value # remove temporary added and get value
dn = resource.delete(:dn) dn = resource.delete(:dn)
# use signup roles if no dn is present raise "Missing 'dn' attribute for remote id '#{@remote_id}'" if dn.blank?
return @signup_role_ids if !dn
if @dn_roles.present?
# 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 ]
# return found 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 roles.present?
# # LDAP is the leading source if
# if there is no role mapping in general the signup roles # a mapping entry is present
# should only get used for newly created users (see .create method) @update_role_ids = roles
# otherwise users might overwrite their local assigned @create_role_ids = roles
# roles with less privileged roles and lock theirselfs out elsif @ldap_config[:unassigned_users] == 'skip_sync'
# throw :no_roles_assigned
# see issue 350#issuecomment-295252402 else
# use_signup_roles
# this method only gets called from the .updated? method end
# so we won't return any roles which will keep the current else
# role assignment for the User use_signup_roles
# the signup roles will be taken in this case direcly in end
# the .create method end
nil
def use_signup_roles
@update_role_ids = nil # use existing
@create_role_ids = @signup_role_ids
end end
def updated?(resource, *_args) def updated?(resource, *_args)
# this is needed for the special case described in the role_ids method resource[:role_ids] = @update_role_ids if @update_role_ids
#
# 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 = false user_found = false
import_class.without_callback(:update, :after, :avatar_for_email_check) do import_class.without_callback(:update, :after, :avatar_for_email_check) do
user_found = super user_found = super
end end
# in case a User was found and we had no roles # in case an User was found and we had no roles
# to set/update we have to note the currently # to set/update we have to note the currently locally
# assigned roles so that our action check won't # assigned roles so that our action check won't
# falsly detect changes to the User # falsly detect changes to the User (from nil to current)
if user_found && update_roles.blank? if user_found && @update_role_ids.blank?
resource[:role_ids] = @resource.role_ids 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 end
user_found user_found
@ -159,11 +164,7 @@ 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 resource[:role_ids] = @create_role_ids
#
# in case we have no role IDs yet we have to fall back to the signup roles
resource[:role_ids] ||= @signup_role_ids
import_class.without_callback(:create, :after, :avatar_for_email_check) do import_class.without_callback(:create, :after, :avatar_for_email_check) do
super super
end end
@ -231,7 +232,6 @@ module Import
updated_by_id: 1, updated_by_id: 1,
) )
end end
end end
end end
end end

View file

@ -17,7 +17,10 @@ module Import
@ldap = ldap @ldap = ldap
user_roles = user_roles(ldap: @ldap, config: config) user_roles = user_roles(ldap: @ldap, config: config)
if config[:unassigned_users].blank? || config[:unassigned_users] == 'sigup_roles'
signup_role_ids = Role.signup_role_ids.sort signup_role_ids = Role.signup_role_ids.sort
end
@dry_run = kargs[:dry_run] @dry_run = kargs[:dry_run]
pre_import_hook([], config, user_roles, signup_role_ids, kargs) pre_import_hook([], config, user_roles, signup_role_ids, kargs)

View file

@ -28,7 +28,9 @@ RSpec.describe Import::Ldap::User do
let(:user_roles) do let(:user_roles) do
{ {
user_entry.dn => [1] user_entry.dn => [
Role.find_by(name: 'Admin').id
]
} }
end end
@ -65,23 +67,6 @@ RSpec.describe Import::Ldap::User do
expect(HttpLog.last.status).to eq('success') expect(HttpLog.last.status).to eq('success')
end end
it 'uses mapped roles from group role' do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(User.last.role_ids).not_to eq(signup_role_ids)
end
it 'uses Signup roles if no group role mapping was found' do
# update old
user_roles[ user_entry.dn ] = [1, 2]
# change dn so no mapping will match
user_entry['dn'] = ['some_unmapped_dn']
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(User.last.role_ids).to eq(signup_role_ids)
end
it 'skips User entries without attributes' do it 'skips User entries without attributes' do
skip_entry = build(:ldap_entry) skip_entry = build(:ldap_entry)
@ -101,6 +86,58 @@ RSpec.describe Import::Ldap::User do
expect(HttpLog.last.status).to eq('failed') expect(HttpLog.last.status).to eq('failed')
end end
context 'role assignment' do
it 'uses mapped roles from group role' do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(User.last.role_ids).not_to eq(signup_role_ids)
end
context 'no mapping entry' do
before(:each) do
# create mapping that won't match
# since dn will change below
# this is needed since if 'user_roles'
# gets called later it will get initialized
# with the changed dn
user_roles[ user_entry.dn ] = [
Role.find_by(name: 'Agent').id,
Role.find_by(name: 'Admin').id
]
# change dn so no mapping will match
user_entry['dn'] = ['some_unmapped_dn']
end
it 'uses signup roles by default' do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(User.last.role_ids).to eq(signup_role_ids)
end
it 'uses signup roles if configured' do
ldap_config[:unassigned_users] = 'sigup_roles'
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
expect(User.last.role_ids).to eq(signup_role_ids)
end
it 'skips user if configured' do
ldap_config[:unassigned_users] = 'skip_sync'
instance = nil
expect do
instance = described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
end.not_to change {
User.count
}
expect(instance.action).to eq(:skipped)
end
end
end
end end
context 'update' do context 'update' do
@ -138,14 +175,6 @@ RSpec.describe Import::Ldap::User 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)
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)
@ -178,5 +207,62 @@ RSpec.describe Import::Ldap::User do
expect(HttpLog.last.status).to eq('failed') expect(HttpLog.last.status).to eq('failed')
end end
context 'no mapping entry' do
before(:each) do
# create mapping that won't match
# since dn will change below
# this is needed since if 'user_roles'
# gets called later it will get initialized
# with the changed dn
user_roles[ user_entry.dn ] = [
Role.find_by(name: 'Agent').id,
Role.find_by(name: 'Admin').id
]
# change dn so no mapping will match
user_entry['dn'] = ['some_unmapped_dn']
end
it 'keeps local roles by default' do
expect do
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
end.not_to change {
User.last.role_ids
}
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
ldap_config[:unassigned_users] = 'skip_sync'
instance = nil
expect do
instance = described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
end.not_to change {
User.count
}
expect(instance.action).to eq(:skipped)
end
end
end end
end end