Replaced old LDAP sync with refactored Sequencer based version 🚀.
This commit is contained in:
parent
e73f75c458
commit
12a63ac17f
10 changed files with 76 additions and 1365 deletions
|
@ -9,7 +9,7 @@ module Integration::ImportJobBase
|
|||
end
|
||||
|
||||
def job_try_create
|
||||
ImportJob.dry_run(name: import_backend_namespace, payload: payload_dry_run)
|
||||
ImportJob.dry_run(name: backend, payload: payload_dry_run)
|
||||
render json: {
|
||||
result: 'ok',
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ module Integration::ImportJobBase
|
|||
end
|
||||
|
||||
def job_start_create
|
||||
if !ImportJob.exists?(name: import_backend_namespace, finished_at: nil)
|
||||
job = ImportJob.create(name: import_backend_namespace, payload: payload_import)
|
||||
if !ImportJob.exists?(name: backend, finished_at: nil)
|
||||
job = ImportJob.create(name: backend)
|
||||
job.delay.start
|
||||
end
|
||||
render json: {
|
||||
|
@ -33,10 +33,6 @@ module Integration::ImportJobBase
|
|||
clean_payload(params.permit!.to_h)
|
||||
end
|
||||
|
||||
def payload_import
|
||||
clean_payload(import_setting)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clean_payload(payload)
|
||||
|
@ -54,31 +50,19 @@ module Integration::ImportJobBase
|
|||
}
|
||||
end
|
||||
|
||||
def import_setting
|
||||
Setting.get(import_setting_name)
|
||||
end
|
||||
|
||||
def import_setting_name
|
||||
"#{import_backend_name.downcase}_config"
|
||||
end
|
||||
|
||||
def import_backend_namespace
|
||||
"Import::#{import_backend_name}"
|
||||
end
|
||||
|
||||
def import_backend_name
|
||||
self.class.name.split('::').last.sub('Controller', '')
|
||||
def backend
|
||||
"Import::#{controller_name.classify}"
|
||||
end
|
||||
|
||||
def job_index(dry_run:, take_finished: true)
|
||||
job = ImportJob.find_by(
|
||||
name: import_backend_namespace,
|
||||
name: backend,
|
||||
dry_run: dry_run,
|
||||
finished_at: nil
|
||||
)
|
||||
if !job && take_finished
|
||||
job = ImportJob.where(
|
||||
name: import_backend_namespace,
|
||||
name: backend,
|
||||
dry_run: dry_run
|
||||
).order(created_at: :desc).limit(1).first
|
||||
end
|
||||
|
|
|
@ -48,7 +48,6 @@ class Integration::ExchangeController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
# currently a workaround till LDAP is migrated to Sequencer
|
||||
def payload_dry_run
|
||||
{
|
||||
ews_attributes: params[:attributes].permit!.to_h,
|
||||
|
@ -57,10 +56,6 @@ class Integration::ExchangeController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def payload_import
|
||||
nil
|
||||
end
|
||||
|
||||
def ews_config
|
||||
{
|
||||
disable_ssl_verify: params[:disable_ssl_verify],
|
||||
|
|
|
@ -9,57 +9,50 @@ class Integration::LdapController < ApplicationController
|
|||
prepend_before_action { authentication_check(permission: 'admin.integration.ldap') }
|
||||
|
||||
def discover
|
||||
ldap = ::Ldap.new(params)
|
||||
answer_with do
|
||||
begin
|
||||
ldap = ::Ldap.new(params)
|
||||
|
||||
render json: {
|
||||
result: 'ok',
|
||||
attributes: ldap.preferences,
|
||||
}
|
||||
rescue => e
|
||||
# workaround for issue #1114
|
||||
if e.message.end_with?(', 48, Inappropriate Authentication')
|
||||
result = {
|
||||
result: 'ok',
|
||||
attributes: {},
|
||||
}
|
||||
else
|
||||
logger.error e
|
||||
result = {
|
||||
result: 'failed',
|
||||
message: e.message,
|
||||
}
|
||||
{
|
||||
attributes: ldap.preferences
|
||||
}
|
||||
rescue => e
|
||||
# workaround for issue #1114
|
||||
raise if !e.message.end_with?(', 48, Inappropriate Authentication')
|
||||
# return empty result
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
render json: result
|
||||
end
|
||||
|
||||
def bind
|
||||
# create single instance so
|
||||
# User and Group don't have to
|
||||
# open new connections
|
||||
ldap = ::Ldap.new(params)
|
||||
user = ::Ldap::User.new(params, ldap: ldap)
|
||||
group = ::Ldap::Group.new(params, ldap: ldap)
|
||||
answer_with do
|
||||
# create single instance so
|
||||
# User and Group don't have to
|
||||
# open new connections
|
||||
ldap = ::Ldap.new(params)
|
||||
user = ::Ldap::User.new(params, ldap: ldap)
|
||||
group = ::Ldap::Group.new(params, ldap: ldap)
|
||||
|
||||
render json: {
|
||||
result: 'ok',
|
||||
{
|
||||
# the order of these calls is relevant!
|
||||
user_filter: user.filter,
|
||||
user_attributes: user.attributes,
|
||||
user_uid: user.uid_attribute,
|
||||
|
||||
# the order of these calls is relevant!
|
||||
user_filter: user.filter,
|
||||
user_attributes: user.attributes,
|
||||
user_uid: user.uid_attribute,
|
||||
# the order of these calls is relevant!
|
||||
group_filter: group.filter,
|
||||
groups: group.list,
|
||||
group_uid: group.uid_attribute,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# the order of these calls is relevant!
|
||||
group_filter: group.filter,
|
||||
groups: group.list,
|
||||
group_uid: group.uid_attribute,
|
||||
}
|
||||
rescue => e
|
||||
logger.error e
|
||||
private
|
||||
|
||||
render json: {
|
||||
result: 'failed',
|
||||
message: e.message,
|
||||
def payload_dry_run
|
||||
{
|
||||
ldap_config: super
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,30 +5,12 @@ require 'ldap/group'
|
|||
|
||||
module Import
|
||||
class Ldap < Import::IntegrationBase
|
||||
|
||||
# Provides the name that is used in texts visible to the user.
|
||||
#
|
||||
# @example
|
||||
# Import::Ldap.display_name
|
||||
# #=> "LDAP"
|
||||
#
|
||||
# return [String]
|
||||
def self.display_name
|
||||
identifier.upcase
|
||||
end
|
||||
include Import::Mixin::Sequence
|
||||
|
||||
private
|
||||
|
||||
def start_import
|
||||
Import::Ldap::UserFactory.reset_statistics
|
||||
|
||||
Import::Ldap::UserFactory.import(
|
||||
config: @import_job.payload,
|
||||
dry_run: @import_job.dry_run,
|
||||
import_job: @import_job
|
||||
)
|
||||
|
||||
@import_job.result = Import::Ldap::UserFactory.statistics
|
||||
def sequence_name
|
||||
'Import::Ldap::Users'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,263 +0,0 @@
|
|||
module Import
|
||||
class Ldap
|
||||
class User < Import::ModelResource
|
||||
|
||||
def remote_id(_resource, *_args)
|
||||
@remote_id
|
||||
end
|
||||
|
||||
def self.lost_map(found_remote_ids)
|
||||
ExternalSync.joins('INNER JOIN users ON (users.id = external_syncs.o_id)')
|
||||
.where(
|
||||
source: source,
|
||||
object: import_class.name,
|
||||
users: {
|
||||
active: true
|
||||
}
|
||||
)
|
||||
.pluck(:source_id, :o_id)
|
||||
.to_h
|
||||
.except(*found_remote_ids)
|
||||
end
|
||||
|
||||
def self.deactivate_lost(lost_ids)
|
||||
# we need to update in slices since some DBs
|
||||
# have a limit for IN length
|
||||
lost_ids.each_slice(5000) do |slice|
|
||||
|
||||
# we need to instanciate every entry and set
|
||||
# the active state this way to send notifications
|
||||
# to the client
|
||||
::User.where(id: slice).each do |user|
|
||||
user.update!(active: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def import(resource, *args)
|
||||
normalized_entry = normalize_entry(resource)
|
||||
|
||||
# extract the uid attribute and store it as
|
||||
# the remote ID so we can access it later
|
||||
# when working with ExternalSync
|
||||
@remote_id = normalized_entry[ @ldap_config[:user_uid].to_sym ]
|
||||
|
||||
super(normalized_entry, *args)
|
||||
end
|
||||
|
||||
def normalize_entry(resource)
|
||||
normalized_entry = resource.to_h
|
||||
|
||||
normalized_entry.each do |key, values|
|
||||
normalized_entry[key] = values.first
|
||||
end
|
||||
|
||||
normalized_entry
|
||||
end
|
||||
|
||||
def create_or_update(resource, *args)
|
||||
result = nil
|
||||
if skip?(resource)
|
||||
ldap_log(
|
||||
action: "skipped -> #{@remote_id}",
|
||||
status: 'success',
|
||||
request: resource,
|
||||
)
|
||||
else
|
||||
catch(:no_roles_assigned) do
|
||||
determine_role_ids(resource)
|
||||
|
||||
result = super(resource, *args)
|
||||
|
||||
ldap_log(
|
||||
action: "#{action} -> #{@resource.login}",
|
||||
status: 'success',
|
||||
request: resource,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def skip?(resource)
|
||||
return true if resource[:login].blank?
|
||||
|
||||
# skip resource if only ignored attributes are set
|
||||
ignored_attributes = %i[login dn created_by_id updated_by_id active]
|
||||
resource.except(*ignored_attributes).values.none?(&:present?)
|
||||
end
|
||||
|
||||
def determine_role_ids(resource)
|
||||
# remove temporary added and get value
|
||||
dn = resource.delete(:dn)
|
||||
raise "Missing 'dn' attribute for remote id '#{@remote_id}'" if dn.blank?
|
||||
|
||||
if @dn_roles.present?
|
||||
# check if roles are mapped for the found dn
|
||||
roles = @dn_roles[ dn.downcase ]
|
||||
|
||||
if roles.present?
|
||||
# LDAP is the leading source if
|
||||
# a mapping entry is present
|
||||
@update_role_ids = roles
|
||||
@create_role_ids = roles
|
||||
elsif @ldap_config[:unassigned_users] == 'skip_sync'
|
||||
throw :no_roles_assigned
|
||||
else
|
||||
use_signup_roles
|
||||
end
|
||||
else
|
||||
use_signup_roles
|
||||
end
|
||||
end
|
||||
|
||||
def use_signup_roles
|
||||
@update_role_ids = nil # use existing
|
||||
@create_role_ids = @signup_role_ids
|
||||
end
|
||||
|
||||
def updated?(resource, *_args)
|
||||
|
||||
resource[:role_ids] = @update_role_ids if @update_role_ids
|
||||
|
||||
user_found = false
|
||||
import_class.without_callback(:update, :after, :avatar_for_email_check) do
|
||||
user_found = super
|
||||
end
|
||||
|
||||
user_found
|
||||
rescue => e
|
||||
ldap_log(
|
||||
action: "update -> #{resource[:login]}",
|
||||
status: 'failed',
|
||||
request: resource,
|
||||
response: e.message,
|
||||
)
|
||||
raise
|
||||
end
|
||||
|
||||
def lookup_existing(resource, *args)
|
||||
instance = super
|
||||
|
||||
return instance if instance.present?
|
||||
|
||||
# in some cases the User will get created in
|
||||
# Zammad before it's created in the LDAP
|
||||
# therefore we have to make a local lookup, too
|
||||
instance = local_lookup(resource)
|
||||
|
||||
# create an external sync entry to connect
|
||||
# the LDAP and local account for future runs
|
||||
if instance.present?
|
||||
external_sync_create(
|
||||
local: instance,
|
||||
remote: resource,
|
||||
)
|
||||
|
||||
store_associations(:before, instance)
|
||||
end
|
||||
|
||||
instance
|
||||
end
|
||||
|
||||
def local_lookup(resource, *_args)
|
||||
instance = import_class.identify(@remote_id)
|
||||
|
||||
if instance.blank?
|
||||
checked_values = [@remote_id]
|
||||
%i[login email].each do |attribute|
|
||||
check_value = resource[attribute]
|
||||
next if check_value.blank?
|
||||
next if checked_values.include?(check_value)
|
||||
instance = import_class.identify(check_value)
|
||||
break if instance.present?
|
||||
checked_values.push(check_value)
|
||||
end
|
||||
end
|
||||
instance
|
||||
end
|
||||
|
||||
def tracked_associations
|
||||
[:role_ids]
|
||||
end
|
||||
|
||||
def create(resource, *_args)
|
||||
resource[:role_ids] = @create_role_ids
|
||||
import_class.without_callback(:create, :after, :avatar_for_email_check) do
|
||||
super
|
||||
end
|
||||
rescue => e
|
||||
ldap_log(
|
||||
action: "create -> #{resource[:login]}",
|
||||
status: 'failed',
|
||||
request: resource,
|
||||
response: e.message,
|
||||
)
|
||||
raise
|
||||
end
|
||||
|
||||
def map(_resource, *_args)
|
||||
mapped = super
|
||||
|
||||
# we have to manually downcase the login and email
|
||||
# to avoid wrong attribute change detection
|
||||
%i[login email].each do |attribute|
|
||||
next if mapped[attribute].blank?
|
||||
mapped[attribute] = mapped[attribute].downcase
|
||||
end
|
||||
|
||||
# we have to add the active state manually
|
||||
# because otherwise disabled instances won't get
|
||||
# re-activated if they should get synced again
|
||||
mapped[:active] = true
|
||||
|
||||
mapped
|
||||
end
|
||||
|
||||
def mapping(*_args)
|
||||
@mapping ||= begin
|
||||
mapping = @ldap_config[:user_attributes]
|
||||
|
||||
# add temporary dn to mapping so we can use it
|
||||
# for the role lookup later and delete it afterwards
|
||||
mapping['dn'] = 'dn'
|
||||
|
||||
# fallback to uid if no login is given via mapping
|
||||
if !mapping.values.include?('login')
|
||||
mapping[ @ldap_config[:user_uid] ] = 'login'
|
||||
end
|
||||
|
||||
mapping
|
||||
end
|
||||
end
|
||||
|
||||
def handle_args(resource, *args)
|
||||
@ldap_config = args.shift
|
||||
@dn_roles = args.shift
|
||||
@signup_role_ids = args.shift
|
||||
|
||||
super(resource, *args)
|
||||
end
|
||||
|
||||
def ldap_log(action:, status:, request:, response: nil)
|
||||
return if @dry_run
|
||||
|
||||
HttpLog.create(
|
||||
direction: 'out',
|
||||
facility: 'ldap',
|
||||
url: action,
|
||||
status: status,
|
||||
ip: nil,
|
||||
request: { content: request.to_json },
|
||||
response: { message: response || status },
|
||||
method: 'tcp',
|
||||
created_by_id: 1,
|
||||
updated_by_id: 1,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,166 +0,0 @@
|
|||
module Import
|
||||
class Ldap
|
||||
module UserFactory
|
||||
extend Import::StatisticalFactory
|
||||
|
||||
def self.import(config: nil, ldap: nil, **kargs)
|
||||
|
||||
# config might be an empty Hash due to the ImportJob payload
|
||||
# store column which will be an empty hash if the content is NULL
|
||||
if config.blank?
|
||||
config = Setting.get('ldap_config')
|
||||
end
|
||||
|
||||
ldap ||= ::Ldap.new(config)
|
||||
|
||||
@config = config
|
||||
@ldap = ldap
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@dry_run = kargs[:dry_run]
|
||||
pre_import_hook([], config, user_roles, signup_role_ids, kargs)
|
||||
|
||||
import_job = kargs[:import_job]
|
||||
import_job_count = 0
|
||||
|
||||
# limit the fetched attributes for an entry to only
|
||||
# those which are needed to improve the performance
|
||||
relevant_attributes = config[:user_attributes].keys
|
||||
relevant_attributes.push('dn')
|
||||
|
||||
@found_lost_remote_ids = []
|
||||
@found_remote_ids = []
|
||||
@ldap.search(config[:user_filter], attributes: relevant_attributes) do |entry|
|
||||
backend_instance = create_instance(entry, config, user_roles, signup_role_ids, kargs)
|
||||
post_import_hook(entry, backend_instance, config, user_roles, signup_role_ids, kargs)
|
||||
|
||||
track_found_remote_ids(backend_instance)
|
||||
|
||||
next if import_job.blank?
|
||||
import_job_count += 1
|
||||
next if import_job_count < 100
|
||||
|
||||
import_job.result = @statistics
|
||||
import_job.save
|
||||
|
||||
import_job_count = 0
|
||||
end
|
||||
|
||||
handle_lost
|
||||
end
|
||||
|
||||
def self.pre_import_hook(_records, *_args)
|
||||
super
|
||||
add_sum_to_statistics
|
||||
end
|
||||
|
||||
def self.add_sum_to_statistics
|
||||
cache_key = "#{@ldap.host}::#{@ldap.port}::#{@ldap.ssl}::#{@ldap.base_dn}::#{@config[:user_filter]}"
|
||||
if !@dry_run
|
||||
sum = Cache.get(cache_key)
|
||||
end
|
||||
|
||||
sum ||= @ldap.count(@config[:user_filter])
|
||||
|
||||
@statistics[:sum] = sum
|
||||
|
||||
return if !@dry_run
|
||||
Cache.write(cache_key, sum, { expires_in: 1.hour })
|
||||
end
|
||||
|
||||
def self.add_to_statistics(backend_instance)
|
||||
super
|
||||
|
||||
# no need to count if no resource was created
|
||||
resource = backend_instance.resource
|
||||
return if resource.blank?
|
||||
|
||||
action = backend_instance.action
|
||||
|
||||
add_resource_role_ids_to_statistics(resource.role_ids, action)
|
||||
|
||||
action
|
||||
end
|
||||
|
||||
def self.add_resource_role_ids_to_statistics(role_ids, action)
|
||||
return if role_ids.blank?
|
||||
|
||||
known_actions = {
|
||||
created: 0,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
}
|
||||
|
||||
@statistics[:role_ids] ||= {}
|
||||
|
||||
role_ids.each do |role_id|
|
||||
|
||||
next if !known_actions.key?(action)
|
||||
|
||||
@statistics[:role_ids][role_id] ||= known_actions.dup
|
||||
|
||||
# exit early if we have an unloggable action
|
||||
break if @statistics[:role_ids][role_id][action].nil?
|
||||
|
||||
@statistics[:role_ids][role_id][action] += 1
|
||||
end
|
||||
end
|
||||
|
||||
def self.user_roles(ldap:, config:)
|
||||
group_config = {
|
||||
filter: config[:group_filter]
|
||||
}
|
||||
|
||||
ldap_group = ::Ldap::Group.new(group_config, ldap: ldap)
|
||||
ldap_group.user_roles(config[:group_role_map])
|
||||
end
|
||||
|
||||
def self.track_found_remote_ids(backend_instance)
|
||||
remote_id = backend_instance.remote_id(nil)
|
||||
@deactivation_actions ||= %i[skipped failed]
|
||||
if @deactivation_actions.include?(backend_instance.action)
|
||||
@found_lost_remote_ids.push(remote_id)
|
||||
else
|
||||
@found_remote_ids.push(remote_id)
|
||||
end
|
||||
end
|
||||
|
||||
def self.handle_lost
|
||||
backend_class = backend_class(nil)
|
||||
lost_map = backend_class.lost_map(@found_remote_ids)
|
||||
|
||||
# disabled count is tracked as a separate number
|
||||
# since they don't have to be in the sum (e.g. deleted in LDAP)
|
||||
@statistics[:deactivated] = lost_map.size
|
||||
|
||||
# skipped deactivated are those who
|
||||
# were found, skipped and will get deactivated
|
||||
skipped_deactivated = @found_lost_remote_ids & lost_map.keys
|
||||
@statistics[:skipped] -= skipped_deactivated.size
|
||||
|
||||
# loop over every lost user ID and add the
|
||||
# deactivated count to the statistics
|
||||
lost_ids = lost_map.values
|
||||
|
||||
lost_ids.each do |user_id|
|
||||
role_ids = ::User.joins(:roles)
|
||||
.where(id: user_id)
|
||||
.pluck(:'roles_users.role_id')
|
||||
|
||||
add_resource_role_ids_to_statistics(role_ids, :deactivated)
|
||||
end
|
||||
|
||||
# deactivate entries only on live syncs
|
||||
return if @dry_run
|
||||
backend_class.deactivate_lost(lost_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,546 +0,0 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Import::Ldap::UserFactory do
|
||||
|
||||
describe '.import' do
|
||||
|
||||
it 'responds to .import' do
|
||||
expect(described_class).to respond_to(:import)
|
||||
end
|
||||
|
||||
it 'imports users matching the configured filter' do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
mocked_entry = build(:ldap_entry)
|
||||
|
||||
mocked_entry['uid'] = ['exampleuid']
|
||||
mocked_entry['email'] = ['example@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
allow(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(mocked_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap
|
||||
)
|
||||
end.to change {
|
||||
User.count
|
||||
}.by(1)
|
||||
end
|
||||
|
||||
it 'deactivates lost users' do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
persistent_entry = build(:ldap_entry)
|
||||
persistent_entry['uid'] = ['exampleuid']
|
||||
persistent_entry['email'] = ['example@example.com']
|
||||
|
||||
lost_entry = build(:ldap_entry)
|
||||
lost_entry['uid'] = ['exampleuid_lost']
|
||||
lost_entry['email'] = ['lost@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry).and_yield(lost_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
end.to change {
|
||||
User.find_by(email: 'lost@example.com').active
|
||||
}
|
||||
end
|
||||
|
||||
it 're-activates previously lost users' do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
persistent_entry = build(:ldap_entry)
|
||||
persistent_entry['uid'] = ['exampleuid']
|
||||
persistent_entry['email'] = ['example@example.com']
|
||||
|
||||
lost_entry = build(:ldap_entry)
|
||||
lost_entry['uid'] = ['exampleuid_lost']
|
||||
lost_entry['email'] = ['lost@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry).and_yield(lost_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry).and_yield(lost_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
end.to change {
|
||||
User.find_by(email: 'lost@example.com').active
|
||||
}
|
||||
end
|
||||
|
||||
it 'deactivates skipped users' do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
},
|
||||
}
|
||||
|
||||
lost_entry = build(:ldap_entry)
|
||||
lost_entry['uid'] = ['exampleuid']
|
||||
lost_entry['email'] = ['example@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(lost_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
# activate skipping
|
||||
config[:unassigned_users] = 'skip_sync'
|
||||
config[:group_role_map] = {
|
||||
'dummy' => %w[1 2],
|
||||
}
|
||||
|
||||
# group user role mapping
|
||||
mocked_entry = build(:ldap_entry)
|
||||
mocked_entry['dn'] = 'dummy'
|
||||
mocked_entry['member'] = ['dummy']
|
||||
expect(mocked_ldap).to receive(:search).and_yield(mocked_entry)
|
||||
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(lost_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
end.to change {
|
||||
User.find_by(email: 'example@example.com').active
|
||||
}
|
||||
end
|
||||
|
||||
context 'dry run' do
|
||||
|
||||
it "doesn't sync users" do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
mocked_entry = build(:ldap_entry)
|
||||
|
||||
mocked_entry['uid'] = ['exampleuid']
|
||||
mocked_entry['email'] = ['example@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(mocked_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
dry_run: true
|
||||
)
|
||||
end.not_to change {
|
||||
User.count
|
||||
}
|
||||
end
|
||||
|
||||
it "doesn't deactivates lost users" do
|
||||
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
persistent_entry = build(:ldap_entry)
|
||||
persistent_entry['uid'] = ['exampleuid']
|
||||
persistent_entry['email'] = ['example@example.com']
|
||||
|
||||
lost_entry = build(:ldap_entry)
|
||||
lost_entry['uid'] = ['exampleuid']
|
||||
lost_entry['email'] = ['example@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry).and_yield(lost_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
dry_run: true
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
expect(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry)
|
||||
|
||||
expect do
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
dry_run: true
|
||||
)
|
||||
end.not_to change {
|
||||
User.count
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.add_to_statistics' do
|
||||
|
||||
it 'responds to .add_to_statistics' do
|
||||
expect(described_class).to respond_to(:add_to_statistics)
|
||||
end
|
||||
|
||||
it 'adds statistics per user role' do
|
||||
|
||||
mocked_backend_instance = double(
|
||||
action: :created,
|
||||
resource: double(
|
||||
role_ids: [1, 2]
|
||||
)
|
||||
)
|
||||
|
||||
# initialize empty statistic
|
||||
described_class.reset_statistics
|
||||
|
||||
described_class.add_to_statistics(mocked_backend_instance)
|
||||
|
||||
expected = {
|
||||
role_ids: {
|
||||
1 => {
|
||||
created: 1,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
},
|
||||
2 => {
|
||||
created: 1,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
},
|
||||
},
|
||||
skipped: 0,
|
||||
created: 1,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
}
|
||||
|
||||
expect(described_class.statistics).to include(expected)
|
||||
end
|
||||
|
||||
it 'adds deactivated users' do
|
||||
config = {
|
||||
user_filter: '(objectClass=user)',
|
||||
group_filter: '(objectClass=group)',
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
|
||||
persistent_entry = build(:ldap_entry)
|
||||
persistent_entry['uid'] = ['exampleuid']
|
||||
persistent_entry['email'] = ['example@example.com']
|
||||
|
||||
lost_entry = build(:ldap_entry)
|
||||
lost_entry['uid'] = ['exampleuid_lost']
|
||||
lost_entry['email'] = ['lost@example.com']
|
||||
|
||||
mocked_ldap = double(
|
||||
host: 'ldap.example.com',
|
||||
port: 636,
|
||||
ssl: true,
|
||||
base_dn: 'dc=example,dc=com'
|
||||
)
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
allow(mocked_ldap).to receive(:count).and_return(2)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry).and_yield(lost_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
# simulate new import
|
||||
described_class.reset_statistics
|
||||
|
||||
# group user role mapping
|
||||
expect(mocked_ldap).to receive(:search)
|
||||
# user counting
|
||||
allow(mocked_ldap).to receive(:count).and_return(1)
|
||||
# user search
|
||||
expect(mocked_ldap).to receive(:search).and_yield(persistent_entry)
|
||||
|
||||
described_class.import(
|
||||
config: config,
|
||||
ldap: mocked_ldap,
|
||||
)
|
||||
|
||||
expected = {
|
||||
skipped: 0,
|
||||
created: 0,
|
||||
updated: 0,
|
||||
unchanged: 1,
|
||||
failed: 0,
|
||||
deactivated: 1,
|
||||
}
|
||||
|
||||
expect(described_class.statistics).to include(expected)
|
||||
end
|
||||
|
||||
it 'skips not created instances' do
|
||||
|
||||
mocked_backend_instance = double(
|
||||
action: :skipped,
|
||||
resource: nil,
|
||||
)
|
||||
|
||||
# initialize empty statistic
|
||||
described_class.reset_statistics
|
||||
|
||||
described_class.add_to_statistics(mocked_backend_instance)
|
||||
|
||||
expected = {
|
||||
skipped: 1,
|
||||
created: 0,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
}
|
||||
|
||||
expect(described_class.statistics).to include(expected)
|
||||
end
|
||||
|
||||
it 'skips unwanted actions instances' do
|
||||
|
||||
mocked_backend_instance = double(
|
||||
action: :skipped,
|
||||
resource: double(
|
||||
role_ids: [1, 2]
|
||||
)
|
||||
)
|
||||
|
||||
# initialize empty statistic
|
||||
described_class.reset_statistics
|
||||
|
||||
described_class.add_to_statistics(mocked_backend_instance)
|
||||
|
||||
expected = {
|
||||
skipped: 1,
|
||||
created: 0,
|
||||
updated: 0,
|
||||
unchanged: 0,
|
||||
failed: 0,
|
||||
deactivated: 0,
|
||||
}
|
||||
|
||||
expect(described_class.statistics).to include(expected)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '.user_roles' do
|
||||
|
||||
it 'responds to .user_roles' do
|
||||
expect(described_class).to respond_to(:user_roles)
|
||||
end
|
||||
|
||||
it 'fetches the user DN to local role mapping' do
|
||||
|
||||
group_dn = 'dn=... admin group...'
|
||||
user_dn = 'dn=... admin user...'
|
||||
|
||||
config = {
|
||||
group_filter: '(objectClass=group)',
|
||||
group_role_map: {
|
||||
group_dn => %w[1 2],
|
||||
}
|
||||
}
|
||||
|
||||
mocked_entry = build(:ldap_entry)
|
||||
|
||||
mocked_entry['dn'] = group_dn
|
||||
mocked_entry['member'] = [user_dn]
|
||||
|
||||
mocked_ldap = double()
|
||||
expect(mocked_ldap).to receive(:search).and_yield(mocked_entry)
|
||||
|
||||
user_roles = described_class.user_roles(
|
||||
ldap: mocked_ldap,
|
||||
config: config,
|
||||
)
|
||||
|
||||
expected = {
|
||||
user_dn => [1, 2]
|
||||
}
|
||||
|
||||
expect(user_roles).to be_a(Hash)
|
||||
expect(user_roles).to eq(expected)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,293 +0,0 @@
|
|||
require 'rails_helper'
|
||||
require 'import/ldap/user'
|
||||
|
||||
RSpec.describe Import::Ldap::User do
|
||||
|
||||
let(:uid) { 'exampleuid' }
|
||||
|
||||
let(:ldap_config) do
|
||||
{
|
||||
user_uid: 'uid',
|
||||
user_attributes: {
|
||||
'uid' => 'login',
|
||||
'email' => 'email',
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:user_entry) do
|
||||
user_entry = build(:ldap_entry)
|
||||
|
||||
user_entry['uid'] = [uid]
|
||||
user_entry['email'] = ['example@example.com']
|
||||
|
||||
user_entry
|
||||
end
|
||||
|
||||
let(:user_roles) do
|
||||
{
|
||||
user_entry.dn => [
|
||||
Role.find_by(name: 'Admin').id,
|
||||
Role.find_by(name: 'Agent').id
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let(:signup_role_ids) do
|
||||
Role.signup_role_ids.sort
|
||||
end
|
||||
|
||||
context 'create' do
|
||||
|
||||
it 'creates users from LDAP Entry' do
|
||||
expect do
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to change {
|
||||
User.count
|
||||
}.by(1).and change {
|
||||
ExternalSync.count
|
||||
}.by(1)
|
||||
end
|
||||
|
||||
it "doesn't contact avatar webservice" do
|
||||
# sadly we can't ensure that there are no
|
||||
# outgoing HTTP calls with WebMock
|
||||
expect(Avatar).not_to receive(:auto_detection)
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end
|
||||
|
||||
it 'creates an HTTP Log entry' do
|
||||
expect do
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to change {
|
||||
HttpLog.count
|
||||
}.by(1)
|
||||
|
||||
expect(HttpLog.last.status).to eq('success')
|
||||
end
|
||||
|
||||
it 'logs failures to HTTP Log' do
|
||||
expect_any_instance_of(User).to receive(:save!).and_raise('SOME ERROR')
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
|
||||
expect(HttpLog.last.status).to eq('failed')
|
||||
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: 'Admin').id,
|
||||
Role.find_by(name: 'Agent').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
|
||||
|
||||
context 'update' do
|
||||
|
||||
before(:each) do
|
||||
user = create(:user,
|
||||
login: uid,
|
||||
role_ids: [
|
||||
Role.find_by(name: 'Agent').id,
|
||||
Role.find_by(name: 'Admin').id
|
||||
])
|
||||
|
||||
ExternalSync.create(
|
||||
source: 'Ldap::User',
|
||||
source_id: uid,
|
||||
object: 'User',
|
||||
o_id: user.id
|
||||
)
|
||||
end
|
||||
|
||||
it 'updates users from LDAP Entry' do
|
||||
expect do
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to not_change {
|
||||
User.count
|
||||
}.and not_change {
|
||||
ExternalSync.count
|
||||
}
|
||||
end
|
||||
|
||||
it "doesn't contact avatar webservice" do
|
||||
# sadly we can't ensure that there are no
|
||||
# outgoing HTTP calls with WebMock
|
||||
expect(Avatar).not_to receive(:auto_detection)
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end
|
||||
|
||||
it 'creates an HTTP Log entry' do
|
||||
expect do
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to change {
|
||||
HttpLog.count
|
||||
}.by(1)
|
||||
|
||||
expect(HttpLog.last.status).to eq('success')
|
||||
end
|
||||
|
||||
it 'finds existing Users without ExternalSync entries' do
|
||||
ExternalSync.find_by(
|
||||
source: 'Ldap::User',
|
||||
source_id: uid,
|
||||
object: 'User',
|
||||
).destroy
|
||||
|
||||
expect do
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to not_change {
|
||||
User.count
|
||||
}.and change {
|
||||
ExternalSync.count
|
||||
}.by(1)
|
||||
end
|
||||
|
||||
it 'logs failures to HTTP Log' do
|
||||
expect_any_instance_of(User).to receive(:save!).and_raise('SOME ERROR')
|
||||
described_class.new(user_entry, ldap_config, user_roles, signup_role_ids)
|
||||
|
||||
expect(HttpLog.last.status).to eq('failed')
|
||||
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 '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
|
||||
|
||||
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!(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
|
||||
|
||||
context 'skipped' do
|
||||
|
||||
it 'skips entries without login' do
|
||||
skip_entry = build(:ldap_entry)
|
||||
instance = nil
|
||||
|
||||
expect do
|
||||
instance = described_class.new(skip_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to not_change {
|
||||
User.count
|
||||
}
|
||||
expect(instance.action).to eq(:skipped)
|
||||
end
|
||||
|
||||
it 'skips entries without attributes' do
|
||||
skip_entry = build(:ldap_entry)
|
||||
skip_entry['uid'] = [uid]
|
||||
instance = nil
|
||||
|
||||
expect do
|
||||
instance = described_class.new(skip_entry, ldap_config, user_roles, signup_role_ids)
|
||||
end.to not_change {
|
||||
User.count
|
||||
}
|
||||
expect(instance.action).to eq(:skipped)
|
||||
end
|
||||
|
||||
it 'logs skips to HTTP Log' do
|
||||
skip_entry = build(:ldap_entry)
|
||||
described_class.new(skip_entry, ldap_config, user_roles, signup_role_ids)
|
||||
|
||||
expect(HttpLog.last.status).to eq('success')
|
||||
expect(HttpLog.last.url).to start_with('skipped')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
require 'rails_helper'
|
||||
require 'lib/import/import_job_backend_examples'
|
||||
|
||||
RSpec.describe Import::Ldap do
|
||||
RSpec.describe Import::Ldap, sequencer: :caller do
|
||||
it_behaves_like 'ImportJob backend'
|
||||
|
||||
describe '.queueable?' do
|
||||
|
@ -32,7 +32,8 @@ RSpec.describe Import::Ldap do
|
|||
|
||||
allow(Setting).to receive(:get).with('ldap_integration').and_return(true)
|
||||
allow(Setting).to receive(:get).with('ldap_config').and_return(true)
|
||||
expect(Import::Ldap::UserFactory).to receive(:import)
|
||||
|
||||
expect_sequence
|
||||
|
||||
instance.start
|
||||
end
|
||||
|
@ -43,7 +44,7 @@ RSpec.describe Import::Ldap do
|
|||
import_job = create(:import_job, dry_run: true)
|
||||
instance = described_class.new(import_job)
|
||||
|
||||
expect(Import::Ldap::UserFactory).to receive(:import)
|
||||
expect_sequence
|
||||
|
||||
instance.start
|
||||
end
|
||||
|
@ -54,7 +55,7 @@ RSpec.describe Import::Ldap do
|
|||
|
||||
allow(Setting).to receive(:get).with('ldap_integration').and_return(false)
|
||||
|
||||
expect(Import::Ldap::UserFactory).not_to receive(:import)
|
||||
expect_no_sequence
|
||||
|
||||
expect do
|
||||
instance.start
|
||||
|
@ -73,7 +74,7 @@ RSpec.describe Import::Ldap do
|
|||
allow(Setting).to receive(:get).with('ldap_integration').and_return(true)
|
||||
allow(Setting).to receive(:get).with('ldap_config').and_return({})
|
||||
|
||||
expect(Import::Ldap::UserFactory).not_to receive(:import)
|
||||
expect_no_sequence
|
||||
|
||||
expect do
|
||||
instance.start
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module SequencerUnit
|
||||
|
||||
def process(parameters, &block)
|
||||
def process(parameters = {}, &block)
|
||||
Sequencer::Unit.process(described_class.name, parameters, &block)
|
||||
end
|
||||
end
|
||||
|
@ -13,7 +13,31 @@ module SequencerSequence
|
|||
end
|
||||
end
|
||||
|
||||
module SequencerCaller
|
||||
|
||||
def expect_sequence(sequence_name = nil)
|
||||
|
||||
expected_method_call = receive(:process)
|
||||
if sequence_name
|
||||
expected_method_call.with(sequence_name)
|
||||
end
|
||||
|
||||
expect(Sequencer).to expected_method_call
|
||||
end
|
||||
|
||||
def expect_no_sequence(sequence_name = nil)
|
||||
|
||||
expected_method_call = receive(:process)
|
||||
if sequence_name
|
||||
expected_method_call.with(sequence_name)
|
||||
end
|
||||
|
||||
expect(Sequencer).not_to expected_method_call
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include SequencerUnit, sequencer: :unit
|
||||
config.include SequencerSequence, sequencer: :sequence
|
||||
config.include SequencerCaller, sequencer: :caller
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue