From a5ee9ae5ae06fcd2843a484b549026e04b63d46f Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 18:56:05 +0700 Subject: [PATCH 01/15] Change travis configuration to test on jRuby --- .travis.yml | 17 ++++++++--------- travis/ldapfluff_up_to_snuff.sh | 12 ------------ 2 files changed, 8 insertions(+), 21 deletions(-) delete mode 100755 travis/ldapfluff_up_to_snuff.sh diff --git a/.travis.yml b/.travis.yml index e20b2b6..1e91279 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,11 @@ sudo: false language: ruby +cache: bundler rvm: - - '1.9.3' - - '2.0.0' - - '2.0.0' - - '2.1.8' - - '2.2.4' - - '2.3.0' - -script: - - "./travis/ldapfluff_up_to_snuff.sh" + - 1.9.3 + - 2.0 + - 2.3 + - ruby + - jruby +before_script: bundle exec rubocop +script: bundle exec rake diff --git a/travis/ldapfluff_up_to_snuff.sh b/travis/ldapfluff_up_to_snuff.sh deleted file mode 100755 index 0dc51f9..0000000 --- a/travis/ldapfluff_up_to_snuff.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e # fail on error - -echo "" -echo "MINITEST" -bundle exec rake - -echo "" -echo "RUBOCOP" -# disable rubocop for now -bundle exec rubocop $(git ls-files | grep ".*\.rb$") Gemfile Rakefile ldap_fluff.gemspec || true From 6cb9359b5a05ee75a8c2f5720db171a889d32636 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 18:58:25 +0700 Subject: [PATCH 02/15] Use gemspec file to manage dependencies --- Gemfile | 13 ++----------- ldap_fluff.gemspec | 33 ++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Gemfile b/Gemfile index 1293be4..0cace49 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,6 @@ -# vim:ft=ruby +# frozen_string_literal: true source 'https://rubygems.org' -if RUBY_VERSION.start_with? '1.9' - gem 'net-ldap', '< 0.13' -end - -unless RUBY_VERSION >= '2.2' - gem 'activesupport', '< 5' -end - +# Specify gem's dependencies in the gemspec file gemspec - -gem 'rubocop', :group => :test if RUBY_VERSION >= '2.0' diff --git a/ldap_fluff.gemspec b/ldap_fluff.gemspec index 5d7bf93..1b9c5fe 100644 --- a/ldap_fluff.gemspec +++ b/ldap_fluff.gemspec @@ -1,24 +1,31 @@ +# frozen_string_literal: true + Gem::Specification.new do |s| s.name = 'ldap_fluff' - s.version = '0.4.7' + s.version = '0.5.0' s.summary = 'LDAP querying tools for Active Directory, FreeIPA and POSIX-style' s.description = 'Simple library for binding & group querying on top of various LDAP implementations' s.homepage = 'https://github.com/theforeman/ldap_fluff' s.license = 'GPLv2' - s.files = Dir['lib/**/*.rb'] + Dir['test/**/*.rb'] + ['README.rdoc', 'LICENSE'] - s.extra_rdoc_files = ['README.rdoc', 'LICENSE'] - s.require_path = 'lib' - s.test_files = Dir['test/**/*.rb'] + s.extra_rdoc_files = %w[README.rdoc LICENSE] + s.files = s.extra_rdoc_files + Dir['lib/**/*.rb'] + + s.require_paths = ['lib'] + s.test_files = Dir['test/**/*.rb'] + + s.authors = ['Jordan O\'Mara', 'Daniel Lobato', 'Petr Chalupa', 'Adam Price', 'Marek Hulan', 'Dominic Cleal'] + s.email = %w[jomara@redhat.com elobatocs@gmail.com pchalupa@redhat.com komidore64@gmail.com mhulan@redhat.com + dominic@cleal.org] + + s.required_ruby_version = '>= 1.9.3' - s.has_rdoc = true - s.author = ['Jordan O\'Mara', 'Daniel Lobato', 'Petr Chalupa', 'Adam Price', 'Marek Hulan', 'Dominic Cleal'] - s.email = %w(jomara@redhat.com elobatocs@gmail.com pchalupa@redhat.com komidore64@gmail.com mhulan@redhat.com dominic@cleal.org) + s.add_dependency 'net-ldap', '~> 0.12' + s.add_dependency 'activesupport' - s.required_ruby_version = ">= 1.9.3" + s.add_development_dependency 'bundler', '>= 1.14' + s.add_development_dependency 'rake', '>= 10.0' - s.add_dependency('net-ldap', '>= 0.3.1') - s.add_dependency('activesupport') - s.add_development_dependency('rake') - s.add_development_dependency('minitest') + s.add_development_dependency 'minitest', '~> 5.0' + s.add_development_dependency 'rubocop' end From 53da29646e5ebf3e52ab7667f60480b71d112d08 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 18:59:41 +0700 Subject: [PATCH 03/15] Use editor configuration file to maintain consistent coding styles --- .editorconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4c1101d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +max_line_length = 120 +trim_trailing_whitespace = true + +[*.rdoc] +max_line_length = off +trim_trailing_whitespace = false From 8e2e3afe2b4f2fdc25ccd28c723a5fd0d642cf08 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 18:59:55 +0700 Subject: [PATCH 04/15] Add development guide into README file --- README.rdoc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.rdoc b/README.rdoc index 6f93f8d..117f055 100644 --- a/README.rdoc +++ b/README.rdoc @@ -62,10 +62,12 @@ Your global configuration must provide information about your LDAP host to funct You can pass these arguments as a hash to LdapFluff to get a valid LdapFluff object. - ldap_config = { :host => "freeipa.localdomain", :port => 389, :encryption => nil, :base_dn => "DC=mydomain,DC=com", - :group_base => "DC=groups,DC=mydomain,DC=com", :attr_login => "uid", :server_type => :free_ipa, - :service_user => "admin", :search_filter => "(objectClass=*)", :service_pass => "mypass", - :anon_queries => false } + ldap_config = { + :host => "freeipa.localdomain", :port => 389, :encryption => nil, :base_dn => "DC=mydomain,DC=com", + :group_base => "DC=groups,DC=mydomain,DC=com", :attr_login => "uid", :server_type => :free_ipa, + :service_user => "admin", :search_filter => "(objectClass=*)", :service_pass => "mypass", + :anon_queries => false + } fluff = LdapFluff.new(ldap_config) fluff.valid_user?("admin") # returns true @@ -99,6 +101,13 @@ ActiveSupport::Notifications. ldap_fluff will use this and also pass it to net- When using Rails, pass `:instrumentation_service => ActiveSupport::Notifications` and then subscribe to, and optionally log events (e.g. https://gist.github.com/mnutt/566725). -=== License +== Development + +After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests. +You can also run `irb -r bundler/setup -r ldap_fluff` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. + +== License ldap_fluff is licensed under the GPLv2. Please read LICENSE for more information. From f75de165a5523c44259b621f43c0f8bbccc1b382 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 19:00:42 +0700 Subject: [PATCH 05/15] Update git-ignore file to ignore `vendor/bundle` path --- .gitignore | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 4bef38f..529bb5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,20 @@ -*.swp -.idea -.bin -.bundle -*.gem +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +/Gemfile.lock .rvmrc .ruby-gemset .ruby-version -Gemfile.lock +*.gem +/vendor/bundle +# Misc. +.DS_Store +*.swp +/.idea/ From c973d1747cba3363f71095a1d8d8e249e37b43fb Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 19:04:27 +0700 Subject: [PATCH 06/15] - Move class `LdapFluff` to the root of Gem lib-path - Correct `Rakefile` to skip helpers while running unit-tests --- Rakefile | 20 +++--- lib/ldap_fluff.rb | 123 ++++++++++++++++++++++++++++++++++- lib/ldap_fluff/ldap_fluff.rb | 100 ---------------------------- 3 files changed, 131 insertions(+), 112 deletions(-) delete mode 100644 lib/ldap_fluff/ldap_fluff.rb diff --git a/Rakefile b/Rakefile index 3c68ce9..4caa98e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,14 +1,12 @@ -require 'rubygems' -require 'rake/testtask' - -# The default task is run if rake is given no explicit arguments. -desc 'Default Task' -task :default => :test +# frozen_string_literal: true -# Test Tasks --------------------------------------------------------- +require 'bundler/gem_tasks' +require 'rake/testtask' -Rake::TestTask.new('test') do |t| - t.libs = %w[lib test] - t.test_files = FileList['test/**/*.rb'] - t.verbose = true +Rake::TestTask.new(:test) do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] end + +task default: :test diff --git a/lib/ldap_fluff.rb b/lib/ldap_fluff.rb index 8c68853..4fa74d2 100644 --- a/lib/ldap_fluff.rb +++ b/lib/ldap_fluff.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + +require 'net/ldap' + require 'ldap_fluff/error' require 'ldap_fluff/config' -require 'ldap_fluff/ldap_fluff' require 'ldap_fluff/generic' require 'ldap_fluff/generic_member_service' require 'ldap_fluff/active_directory' @@ -11,3 +14,121 @@ require 'ldap_fluff/freeipa' require 'ldap_fluff/freeipa_member_service' require 'ldap_fluff/freeipa_netgroup_member_service' + +class LdapFluff + # @!attribute [rw] ldap + # @return [Generic] + # @!attribute [rw] instrumentation_service + # @return [#instrument] + attr_accessor :ldap, :instrumentation_service + + def initialize(config = {}) + config = LdapFluff::Config.new(config) + + @ldap = + case config.server_type + when :posix + Posix.new(config) + when :active_directory + ActiveDirectory.new(config) + when :free_ipa + FreeIPA.new(config) + else + raise 'unknown server_type' + end + + @instrumentation_service = config.instrumentation_service + end + + # @param [String] uid + # @param [String] password + # @return [Boolean] + def authenticate?(uid, password) + instrument('authenticate.ldap_fluff', uid: uid) do |payload| + if password.nil? || password.empty? + false + else + !!@ldap.bind?(uid, password) + end + end + end + + def test + instrument('test.ldap_fluff') do |payload| + @ldap.ldap.open {} + end + end + + # @param [String] gid + # @return [Array] a list of users for a given gid + def user_list(gid) + instrument('user_list.ldap_fluff', gid: gid) do |payload| + @ldap.users_for_gid(gid) + end + end + + # @param [String] uid + # @return [Array] a list of groups for a given uid + def group_list(uid) + instrument('group_list.ldap_fluff', uid: uid) do |payload| + @ldap.groups_for_uid(uid) + end + end + + # @param [String] uid + # @return [Boolean] true if a user is in all of the groups in grouplist + def is_in_groups?(uid, grouplist) + instrument('is_in_groups?.ldap_fluff', uid: uid, grouplist: grouplist) do |payload| + @ldap.is_in_groups(uid, grouplist, true) + end + end + + # @param [String] uid + # @return [Boolean] true if uid exists + def valid_user?(uid) + instrument('valid_user?.ldap_fluff', uid: uid) do |payload| + @ldap.user_exists? uid + end + end + + # @param [String] gid + # @return [Boolean] true if group exists + def valid_group?(gid) + instrument('valid_group?.ldap_fluff', gid: gid) do |payload| + @ldap.group_exists? gid + end + end + + # @param [String] uid + # @return [Array, Net::LDAP::Entry] + def find_user(uid) + instrument('find_user.ldap_fluff', uid: uid) do |payload| + @ldap.member_service.find_user(uid) + end + end + + # @param [String] gid + # @return [Array, Net::LDAP::Entry] + def find_group(gid) + instrument('find_group.ldap_fluff', gid: gid) do |payload| + @ldap.member_service.find_group(gid) + end + end + + private + + # @param [String] event + # @param [Hash] payload + # @yieldreturn [Hash] + def instrument(event, payload = {}) + payload = payload ? payload.dup : {} + + if instrumentation_service + instrumentation_service.instrument(event, payload) do |payload| + payload[:result] = yield(payload) if block_given? + end + elsif block_given? + yield(payload) + end + end +end diff --git a/lib/ldap_fluff/ldap_fluff.rb b/lib/ldap_fluff/ldap_fluff.rb deleted file mode 100644 index 987fce8..0000000 --- a/lib/ldap_fluff/ldap_fluff.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'rubygems' -require 'net/ldap' - -class LdapFluff - attr_accessor :ldap, :instrumentation_service - - def initialize(config = {}) - config = LdapFluff::Config.new(config) - case config.server_type - when :posix - @ldap = Posix.new(config) - when :active_directory - @ldap = ActiveDirectory.new(config) - when :free_ipa - @ldap = FreeIPA.new(config) - else - raise 'unknown server_type' - end - @instrumentation_service = config.instrumentation_service - end - - def authenticate?(uid, password) - instrument('authenticate.ldap_fluff', :uid => uid) do |payload| - if password.nil? || password.empty? - false - else - !!@ldap.bind?(uid, password) - end - end - end - - def test - instrument('test.ldap_fluff') do |payload| - @ldap.ldap.open {} - end - end - - # return a list[] of users for a given gid - def user_list(gid) - instrument('user_list.ldap_fluff', :gid => gid) do |payload| - @ldap.users_for_gid(gid) - end - end - - # return a list[] of groups for a given uid - def group_list(uid) - instrument('group_list.ldap_fluff', :uid => uid) do |payload| - @ldap.groups_for_uid(uid) - end - end - - # return true if a user is in all of the groups - # in grouplist - def is_in_groups?(uid, grouplist) - instrument('is_in_groups?.ldap_fluff', :uid => uid, :grouplist => grouplist) do |payload| - @ldap.is_in_groups(uid, grouplist, true) - end - end - - # return true if uid exists - def valid_user?(uid) - instrument('valid_user?.ldap_fluff', :uid => uid) do |payload| - @ldap.user_exists? uid - end - end - - # return true if group exists - def valid_group?(gid) - instrument('valid_group?.ldap_fluff', :gid => gid) do |payload| - @ldap.group_exists? gid - end - end - - # return ldap entry - def find_user(uid) - instrument('find_user.ldap_fluff', :uid => uid) do |payload| - @ldap.member_service.find_user(uid) - end - end - - # return ldap entry - def find_group(gid) - instrument('find_group.ldap_fluff', :gid => gid) do |payload| - @ldap.member_service.find_group(gid) - end - end - - private - - def instrument(event, payload = {}) - payload = (payload || {}).dup - if instrumentation_service - instrumentation_service.instrument(event, payload) do |payload| - payload[:result] = yield(payload) if block_given? - end - else - yield(payload) if block_given? - end - end -end From 83049262eae33948ad609c73ae174c03981867ba Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 19:13:36 +0700 Subject: [PATCH 07/15] Update `rubocop` configuration file to use almost rules --- .rubocop.yml | 96 +++++++++++----------------------------------------- 1 file changed, 20 insertions(+), 76 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 397c814..eac33e9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,92 +1,36 @@ -LineLength: - Max: 120 - -ParameterLists: - Max: 5 - -MethodLength: - Max: 15 - -CollectionMethods: - # remove this once we pick either map or collect, inject or reduce, etc - PreferredMethods: {} +AllCops: + TargetRubyVersion: 2.3 -StringLiterals: +Style/Documentation: Enabled: false -SpaceAroundBraces: - Enabled: false # remove me - -RedundantSelf: - Enabled: false # remove me - -Encoding: - Enabled: false # remove me? - -RedundantReturn: - Enabled: false # remove me - -SpaceInsideHashLiteralBraces: - Enabled: false # remove me - -Void: - Enabled: false # remove me - -BlockNesting: - Enabled: false # remove me - -AvoidPerlBackrefs: - Enabled: false # remove me - -Documentation: - Enabled: false # remove me - -# end TODO - -# AllCops: -# Excludes: - -SpaceInsideHashLiteralBraces: - EnforcedStyleIsWithSpaces: false - -HashSyntax: - Enabled: false # don't force 1.9 hash syntax - -SpaceInsideHashLiteralBraces: - Enabled: false # allow spaces (eg { :a => 1 }) - -LeadingCommentSpace: - Enabled: false - -IfUnlessModifier: - Enabled: false +Metrics/LineLength: + Max: 120 -RescueModifier: +Gemspec/RequiredRubyVersion: Enabled: false -AssignmentInCondition: +Style/ExpandPathArguments: Enabled: false -FavorUnlessOverNegatedIf: - Enabled: false +Style/PercentLiteralDelimiters: + PreferredDelimiters: + '%w': '[]' -WhileUntilModifier: +Style/SymbolArray: Enabled: false -AlignParameters: - Enabled: false # don't care if parameters are not aligned - -ParenthesesAroundCondition: - Enabled: false +Naming/UncommunicativeMethodParamName: + AllowedNames: + - cn + - dn -DotPosition: +Style/ClassAndModuleChildren: Enabled: false -Lambda: - Enabled: false # don't require -> for single line lambdas - -HashMethods: - Enabled: false +Metrics/ClassLength: + Exclude: + - 'test/**/*' -ReduceArguments: +Layout/EndOfLine: Enabled: false From 6584b5aaa0a7fb286de0092ae97b7665bdd44103 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 19:21:15 +0700 Subject: [PATCH 08/15] Move `ldap_test_helper` to the test root as a unit-test entrypoint --- test/ad_member_services_test.rb | 47 +++++++-------- test/ad_test.rb | 43 +++++++------- test/config_test.rb | 14 +++-- test/ipa_member_services_test.rb | 21 +++---- test/ipa_netgroup_member_services_test.rb | 21 +++---- test/ipa_test.rb | 42 +++++++------- test/ldap_test.rb | 25 ++++---- test/{lib => }/ldap_test_helper.rb | 64 ++++++++++----------- test/posix_member_services_test.rb | 32 ++++++----- test/posix_netgroup_member_services_test.rb | 32 ++++++----- test/posix_test.rb | 32 ++++++----- 11 files changed, 193 insertions(+), 180 deletions(-) rename test/{lib => }/ldap_test_helper.rb (56%) diff --git a/test/ad_member_services_test.rb b/test/ad_member_services_test.rb index c58afc8..e9f21b5 100644 --- a/test/ad_member_services_test.rb +++ b/test/ad_member_services_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestADMemberService < MiniTest::Test include LdapTestHelper @@ -10,40 +12,40 @@ def setup end def basic_user - @ldap.expect(:search, ad_user_payload, [:filter => ad_name_filter("john")]) - @ldap.expect(:search, ad_parent_payload(1), [:base => ad_group_dn, :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, ad_user_payload, [filter: ad_name_filter('john')]) + @ldap.expect(:search, ad_parent_payload(1), [base: ad_group_dn, scope: 0, attributes: ['memberof']]) end def basic_group - @ldap.expect(:search, ad_group_payload, [:filter => ad_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, ad_group_payload, [filter: ad_group_filter('broze'), base: @config.group_base]) end def nest_deep(n) # add all the expects 1.upto(n-1) do |i| - @ldap.expect(:search, ad_parent_payload(i + 1), [:base => ad_group_dn("bros#{i}"), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, ad_parent_payload(i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) end # terminate or we loop FOREVER - @ldap.expect(:search, [], [:base => ad_group_dn("bros#{n}"), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, [], [base: ad_group_dn("bros#{n}"), scope: 0, attributes: ['memberof']]) end def double_nested(n) # add all the expects 1.upto(n - 1) do |i| - @ldap.expect(:search, ad_double_payload(i + 1), [:base => ad_group_dn("bros#{i}"), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, ad_double_payload(i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) end # terminate or we loop FOREVER - @ldap.expect(:search, [], [:base => ad_group_dn("bros#{n}"), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, [], [base: ad_group_dn("bros#{n}"), scope: 0, attributes: ['memberof']]) (n - 1).downto(1) do |j| - @ldap.expect(:search, [], [:base => ad_group_dn("broskies#{j + 1}"), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, [], [base: ad_group_dn("broskies#{j + 1}"), scope: 0, attributes: ['memberof']]) end end def test_find_user basic_user - @ldap.expect(:search, [], [:base => ad_group_dn('bros1'), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, [], [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) @adms.ldap = @ldap - assert_equal(%w(group bros1), @adms.find_user_groups("john")) + assert_equal(%w[group bros1], @adms.find_user_groups('john')) @ldap.verify end @@ -51,17 +53,17 @@ def test_nested_groups basic_user # basic user is memberof 'group'... and 'group' is memberof 'bros1' # now make 'bros1' be memberof 'group' again - @ldap.expect(:search, ad_user_payload, [:base => ad_group_dn('bros1'), :scope => 0, :attributes => ['memberof']]) + @ldap.expect(:search, ad_user_payload, [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) @adms.ldap = @ldap - assert_equal(%w(group bros1), @adms.find_user_groups("john")) + assert_equal(%w[group bros1], @adms.find_user_groups('john')) @ldap.verify end def test_missing_user - @ldap.expect(:search, nil, [:filter => ad_name_filter("john")]) + @ldap.expect(:search, nil, [filter: ad_name_filter('john')]) @adms.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do - @adms.find_user_groups("john").data + @adms.find_user_groups('john').data end @ldap.verify end @@ -87,10 +89,10 @@ def test_nil_payload end def test_empty_user - @ldap.expect(:search, [], [:filter => ad_name_filter("john")]) + @ldap.expect(:search, [], [filter: ad_name_filter('john')]) @adms.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do - @adms.find_user_groups("john").data + @adms.find_user_groups('john').data end @ldap.verify end @@ -102,7 +104,7 @@ def test_find_good_user end def test_find_missing_user - @ldap.expect(:search, nil, [:filter => ad_name_filter("john")]) + @ldap.expect(:search, nil, [filter: ad_name_filter('john')]) @adms.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_user('john') @@ -116,7 +118,7 @@ def test_find_good_group end def test_find_missing_group - @ldap.expect(:search, nil, [:filter => ad_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, nil, [filter: ad_group_filter('broze'), base: @config.group_base]) @adms.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException) do @adms.find_group('broze') @@ -124,7 +126,7 @@ def test_find_missing_group end def test_find_by_dn - @ldap.expect(:search, [:result], [:filter => Net::LDAP::Filter.eq('cn', 'Foo Bar'), :base => 'dc=example,dc=com']) + @ldap.expect(:search, [:result], [filter: Net::LDAP::Filter.eq('cn', 'Foo Bar'), base: 'dc=example,dc=com']) @adms.ldap = @ldap assert_equal([:result], @adms.find_by_dn('cn=Foo Bar,dc=example,dc=com')) @ldap.verify @@ -135,14 +137,14 @@ def test_find_by_dn_comma_in_cn # returned by the server in answer to a group membership query with # backslashes before the commas in the CNs. Such escaped commas should not # be used when splitting the DN. - @ldap.expect(:search, [:result], [:filter => Net::LDAP::Filter.eq('cn', 'Bar, Foo'), :base => 'dc=example,dc=com']) + @ldap.expect(:search, [:result], [filter: Net::LDAP::Filter.eq('cn', 'Bar, Foo'), base: 'dc=example,dc=com']) @adms.ldap = @ldap assert_equal([:result], @adms.find_by_dn('cn=Bar\, Foo,dc=example,dc=com')) @ldap.verify end def test_find_by_dn_missing_entry - @ldap.expect(:search, nil, [:filter => Net::LDAP::Filter.eq('cn', 'Foo Bar'), :base => 'dc=example,dc=com']) + @ldap.expect(:search, nil, [filter: Net::LDAP::Filter.eq('cn', 'Foo Bar'), base: 'dc=example,dc=com']) @adms.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_by_dn('cn=Foo Bar,dc=example,dc=com') @@ -160,5 +162,4 @@ def test_get_login_from_entry_missing_attr entry = Net::LDAP::Entry.new('Example User') assert_nil(@adms.get_login_from_entry(entry)) end - end diff --git a/test/ad_test.rb b/test/ad_test.rb index 28ebf1d..12cd6db 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestAD < MiniTest::Test include LdapTestHelper @@ -10,13 +12,13 @@ def setup # default setup for service bind users def service_bind - @ldap.expect(:auth, nil, %w(service pass)) + @ldap.expect(:auth, nil, %w[service pass]) super end def test_good_bind # no expectation on the service account - @ldap.expect(:auth, nil, ['EXAMPLE\\internet', "password"]) + @ldap.expect(:auth, nil, ['EXAMPLE\\internet', 'password']) @ldap.expect(:bind, true) @ad.ldap = @ldap assert_equal(@ad.bind?('EXAMPLE\\internet', 'password'), true) @@ -25,7 +27,7 @@ def test_good_bind def test_good_bind_with_dn # no expectation on the service account - @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), "password"]) + @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), 'password']) @ldap.expect(:bind, true) @ad.ldap = @ldap assert_equal(@ad.bind?(ad_user_dn('Internet User'), 'password'), true) @@ -37,33 +39,33 @@ def test_good_bind_with_account_name @md = MiniTest::Mock.new user_result = MiniTest::Mock.new user_result.expect(:dn, ad_user_dn('Internet User')) - @md.expect(:find_user, [user_result], %w(internet)) + @md.expect(:find_user, [user_result], %w[internet]) @ad.member_service = @md service_bind - @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), "password"]) + @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), 'password']) @ldap.expect(:bind, true) assert_equal(@ad.bind?('internet', 'password'), true) @ldap.verify end def test_bad_bind - @ldap.expect(:auth, nil, %w(EXAMPLE\\internet password)) + @ldap.expect(:auth, nil, %w[EXAMPLE\\internet password]) @ldap.expect(:bind, false) @ad.ldap = @ldap - assert_equal(@ad.bind?("EXAMPLE\\internet", "password"), false) + assert_equal(@ad.bind?('EXAMPLE\\internet', 'password'), false) @ldap.verify end def test_groups service_bind basic_user - assert_equal(@ad.groups_for_uid('john'), %w(bros)) + assert_equal(@ad.groups_for_uid('john'), %w[bros]) end def test_bad_user service_bind md = MiniTest::Mock.new - md.expect(:find_user_groups, nil, %w(john)) + md.expect(:find_user_groups, nil, %w[john]) def md.find_user_groups(*args) raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException end @@ -72,7 +74,7 @@ def md.find_user_groups(*args) end def test_bad_service_user - @ldap.expect(:auth, nil, %w(service pass)) + @ldap.expect(:auth, nil, %w[service pass]) @ldap.expect(:bind, false) @ad.ldap = @ldap assert_raises(LdapFluff::ActiveDirectory::UnauthenticatedException) do @@ -83,31 +85,31 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ad.is_in_groups("john", %w(bros), false), true) + assert_equal(@ad.is_in_groups('john', %w[bros], false), true) end def test_is_some_groups service_bind basic_user - assert_equal(@ad.is_in_groups("john", %w(bros buds), false), true) + assert_equal(@ad.is_in_groups('john', %w[bros buds], false), true) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ad.is_in_groups("john", %w(bros buds), true), false) + assert_equal(@ad.is_in_groups('john', %w[bros buds], true), false) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ad.is_in_groups("john", %w(broskies), false), false) + assert_equal(@ad.is_in_groups('john', %w[broskies], false), false) end def test_group_subset service_bind bigtime_user - assert_equal(@ad.is_in_groups("john", %w(broskies), true), true) + assert_equal(@ad.is_in_groups('john', %w[broskies], true), true) end def test_subgroups_in_groups_are_ignored @@ -125,7 +127,7 @@ def md.find_by_dn(dn) def test_user_exists md = MiniTest::Mock.new - md.expect(:find_user, 'notnilluser', %w(john)) + md.expect(:find_user, 'notnilluser', %w[john]) @ad.member_service = md service_bind assert(@ad.user_exists?('john')) @@ -133,7 +135,7 @@ def test_user_exists def test_missing_user md = MiniTest::Mock.new - md.expect(:find_user, nil, %w(john)) + md.expect(:find_user, nil, %w[john]) def md.find_user(uid) raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException end @@ -144,7 +146,7 @@ def md.find_user(uid) def test_group_exists md = MiniTest::Mock.new - md.expect(:find_group, 'notnillgroup', %w(broskies)) + md.expect(:find_group, 'notnillgroup', %w[broskies]) @ad.member_service = md service_bind assert(@ad.group_exists?('broskies')) @@ -152,7 +154,7 @@ def test_group_exists def test_missing_group md = MiniTest::Mock.new - md.expect(:find_group, nil, %w(broskies)) + md.expect(:find_group, nil, %w[broskies]) def md.find_group(uid) raise LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException end @@ -208,5 +210,4 @@ def test_find_users_with_empty_nested_group assert_equal @ad.users_for_gid('foremaners'), ['testuser'] md.verify end - end diff --git a/test/config_test.rb b/test/config_test.rb index 890aa06..d28762a 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -1,30 +1,32 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class ConfigTest < MiniTest::Test include LdapTestHelper def test_unsupported_type - assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new(config_hash.update :server_type => 'inactive_directory') } + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new(config_hash.update server_type: 'inactive_directory') } end def test_load_posix - ldap = LdapFluff.new(config_hash.update :server_type => 'posix') + ldap = LdapFluff.new(config_hash.update server_type: 'posix') assert_instance_of LdapFluff::Posix, ldap.ldap end def test_load_ad - ldap = LdapFluff.new(config_hash.update :server_type => 'active_directory') + ldap = LdapFluff.new(config_hash.update server_type: 'active_directory') assert_instance_of LdapFluff::ActiveDirectory, ldap.ldap end def test_load_free_ipa - ldap = LdapFluff.new(config_hash.update :server_type => 'free_ipa') + ldap = LdapFluff.new(config_hash.update server_type: 'free_ipa') assert_instance_of LdapFluff::FreeIPA, ldap.ldap end def test_instrumentation_service is = Object.new - net_ldap = LdapFluff.new(config_hash.update :instrumentation_service => is).ldap.ldap + net_ldap = LdapFluff.new(config_hash.update instrumentation_service: is).ldap.ldap assert_equal is, net_ldap.send(:instrumentation_service) end end diff --git a/test/ipa_member_services_test.rb b/test/ipa_member_services_test.rb index 52dd0bb..8d65033 100644 --- a/test/ipa_member_services_test.rb +++ b/test/ipa_member_services_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestIPAMemberService < MiniTest::Test include LdapTestHelper @@ -9,25 +11,25 @@ def setup end def basic_user - @ldap.expect(:search, ipa_user_payload, [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) end def basic_group - @ldap.expect(:search, ipa_group_payload, [:filter => ipa_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, ipa_group_payload, [filter: ipa_group_filter('broze'), base: @config.group_base]) end def test_find_user basic_user @ipams.ldap = @ldap - assert_equal(%w(group bros), @ipams.find_user_groups("john")) + assert_equal(%w[group bros], @ipams.find_user_groups('john')) @ldap.verify end def test_missing_user - @ldap.expect(:search, nil, [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) @ipams.ldap = @ldap assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do - @ipams.find_user_groups("john").data + @ipams.find_user_groups('john').data end @ldap.verify end @@ -35,7 +37,7 @@ def test_missing_user def test_no_groups entry = Net::LDAP::Entry.new entry['memberof'] = [] - @ldap.expect(:search, [ Net::LDAP::Entry.new, entry ], [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, [ Net::LDAP::Entry.new, entry ], [filter: ipa_name_filter('john')]) @ipams.ldap = @ldap assert_equal([], @ipams.find_user_groups('john')) @ldap.verify @@ -48,7 +50,7 @@ def test_find_good_user end def test_find_missing_user - @ldap.expect(:search, nil, [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) @ipams.ldap = @ldap assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do @ipams.find_user('john') @@ -62,11 +64,10 @@ def test_find_good_group end def test_find_missing_group - @ldap.expect(:search, nil, [:filter => ipa_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: @config.group_base]) @ipams.ldap = @ldap assert_raises(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) do @ipams.find_group('broze') end end - end diff --git a/test/ipa_netgroup_member_services_test.rb b/test/ipa_netgroup_member_services_test.rb index aef04ad..2d419da 100644 --- a/test/ipa_netgroup_member_services_test.rb +++ b/test/ipa_netgroup_member_services_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestIPANetgroupMemberService < MiniTest::Test include LdapTestHelper @@ -10,11 +12,11 @@ def setup end def basic_user - @ldap.expect(:search, ipa_user_payload, [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) end def basic_group - @ldap.expect(:search, ipa_netgroup_payload('broze'), [:filter => ipa_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, ipa_netgroup_payload('broze'), [filter: ipa_group_filter('broze'), base: @config.group_base]) end def test_find_user @@ -25,7 +27,7 @@ def test_find_user end def test_find_missing_user - @ldap.expect(:search, nil, [:filter => ipa_name_filter("john")]) + @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) @ipams.ldap = @ldap assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do @ipams.find_user('john') @@ -34,8 +36,8 @@ def test_find_missing_user def test_find_user_groups response = ipa_netgroup_payload('bros', ['(,john,)', '(,joe,)']) - @ldap.expect(:search, response, [:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - :base => @config.group_base]) + @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), + base: @config.group_base]) @ipams.ldap = @ldap assert_equal(['bros'], @ipams.find_user_groups('john')) @@ -44,8 +46,8 @@ def test_find_user_groups def test_find_no_user_groups response = ipa_netgroup_payload('bros', ['(,joe,)']) - @ldap.expect(:search, response, [:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - :base => @config.group_base]) + @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), + base: @config.group_base]) @ipams.ldap = @ldap assert_equal([], @ipams.find_user_groups('john')) @ldap.verify @@ -58,11 +60,10 @@ def test_find_group end def test_find_missing_group - @ldap.expect(:search, nil, [:filter => ipa_group_filter("broze"), :base => @config.group_base]) + @ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: @config.group_base]) @ipams.ldap = @ldap assert_raises(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) do @ipams.find_group('broze') end end - end diff --git a/test/ipa_test.rb b/test/ipa_test.rb index 657800c..f26aea3 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestIPA < MiniTest::Test include LdapTestHelper @@ -10,7 +12,7 @@ def setup # default setup for service bind users def service_bind - @ldap.expect(:auth, nil, [ipa_user_bind('service'), "pass"]) + @ldap.expect(:auth, nil, [ipa_user_bind('service'), 'pass']) super end @@ -19,10 +21,10 @@ def test_good_bind @md = MiniTest::Mock.new user_result = MiniTest::Mock.new user_result.expect(:dn, ipa_user_bind('internet')) - @md.expect(:find_user, [user_result], %w(internet)) + @md.expect(:find_user, [user_result], %w[internet]) @ipa.member_service = @md service_bind - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), "password"]) + @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) @ldap.expect(:bind, true) assert_equal(@ipa.bind?('internet', 'password'), true) @ldap.verify @@ -30,7 +32,7 @@ def test_good_bind def test_good_bind_with_dn # no expectation on the service account - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), "password"]) + @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) @ldap.expect(:bind, true) @ipa.ldap = @ldap assert_equal(@ipa.bind?(ipa_user_bind('internet'), 'password'), true) @@ -38,23 +40,23 @@ def test_good_bind_with_dn end def test_bad_bind - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), "password"]) + @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) @ldap.expect(:bind, false) @ipa.ldap = @ldap - assert_equal(@ipa.bind?(ipa_user_bind("internet"), "password"), false) + assert_equal(@ipa.bind?(ipa_user_bind('internet'), 'password'), false) @ldap.verify end def test_groups service_bind basic_user - assert_equal(@ipa.groups_for_uid('john'), %w(bros)) + assert_equal(@ipa.groups_for_uid('john'), %w[bros]) end def test_bad_user service_bind @md = MiniTest::Mock.new - @md.expect(:find_user_groups, nil, %w(john)) + @md.expect(:find_user_groups, nil, %w[john]) def @md.find_user_groups(*args) raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException end @@ -63,7 +65,7 @@ def @md.find_user_groups(*args) end def test_bad_service_user - @ldap.expect(:auth, nil, [ipa_user_bind('service'), "pass"]) + @ldap.expect(:auth, nil, [ipa_user_bind('service'), 'pass']) @ldap.expect(:bind, false) @ipa.ldap = @ldap assert_raises(LdapFluff::FreeIPA::UnauthenticatedException) do @@ -74,42 +76,42 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ipa.is_in_groups("john", %w(bros), false), true) + assert_equal(@ipa.is_in_groups('john', %w[bros], false), true) end def test_is_some_groups service_bind basic_user - assert_equal(@ipa.is_in_groups("john", %w(bros buds), false), true) + assert_equal(@ipa.is_in_groups('john', %w[bros buds], false), true) end def test_is_in_all_groupss service_bind bigtime_user - assert_equal(true, @ipa.is_in_groups("john", %w(broskies bros), true)) + assert_equal(true, @ipa.is_in_groups('john', %w[broskies bros], true)) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ipa.is_in_groups("john", %w(bros buds), true), false) + assert_equal(@ipa.is_in_groups('john', %w[bros buds], true), false) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ipa.is_in_groups("john", %w(broskies), false), false) + assert_equal(@ipa.is_in_groups('john', %w[broskies], false), false) end def test_group_subset service_bind bigtime_user - assert_equal(@ipa.is_in_groups('john', %w(broskies), true), true) + assert_equal(@ipa.is_in_groups('john', %w[broskies], true), true) end def test_user_exists @md = MiniTest::Mock.new - @md.expect(:find_user, 'notnilluser', %w(john)) + @md.expect(:find_user, 'notnilluser', %w[john]) @ipa.member_service = @md service_bind assert(@ipa.user_exists?('john')) @@ -117,7 +119,7 @@ def test_user_exists def test_missing_user @md = MiniTest::Mock.new - @md.expect(:find_user, nil, %w(john)) + @md.expect(:find_user, nil, %w[john]) def @md.find_user(uid) raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException end @@ -128,7 +130,7 @@ def @md.find_user(uid) def test_group_exists @md = MiniTest::Mock.new - @md.expect(:find_group, 'notnillgroup', %w(broskies)) + @md.expect(:find_group, 'notnillgroup', %w[broskies]) @ipa.member_service = @md service_bind assert(@ipa.group_exists?('broskies')) @@ -136,7 +138,7 @@ def test_group_exists def test_missing_group @md = MiniTest::Mock.new - @md.expect(:find_group, nil, %w(broskies)) + @md.expect(:find_group, nil, %w[broskies]) def @md.find_group(uid) raise LdapFluff::FreeIPA::MemberService::GIDNotFoundException end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index c368be6..90a2fd3 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestLDAP < MiniTest::Test include LdapTestHelper @@ -9,42 +11,42 @@ def setup end def test_bind - @ldap.expect(:bind?, true, %w(john password)) + @ldap.expect(:bind?, true, %w[john password]) @fluff.ldap = @ldap - assert_equal(@fluff.authenticate?("john", "password"), true) + assert_equal(@fluff.authenticate?('john', 'password'), true) @ldap.verify end def test_groups - @ldap.expect(:groups_for_uid, %w(bros), %w(john)) + @ldap.expect(:groups_for_uid, %w[bros], %w[john]) @fluff.ldap = @ldap - assert_equal(@fluff.group_list('john'), %w(bros)) + assert_equal(@fluff.group_list('john'), %w[bros]) @ldap.verify end def test_group_membership - @ldap.expect(:is_in_groups, false, ['john', %w(broskies girlfriends), true]) + @ldap.expect(:is_in_groups, false, ['john', %w[broskies girlfriends], true]) @fluff.ldap = @ldap - assert_equal(@fluff.is_in_groups?('john', %w(broskies girlfriends)), false) + assert_equal(@fluff.is_in_groups?('john', %w[broskies girlfriends]), false) @ldap.verify end def test_valid_user - @ldap.expect(:user_exists?, true, %w(john)) + @ldap.expect(:user_exists?, true, %w[john]) @fluff.ldap = @ldap assert(@fluff.valid_user?('john')) @ldap.verify end def test_valid_group - @ldap.expect(:group_exists?, true, %w(broskies)) + @ldap.expect(:group_exists?, true, %w[broskies]) @fluff.ldap = @ldap assert(@fluff.valid_group?('broskies')) @ldap.verify end def test_invalid_group - @ldap.expect(:group_exists?, false, %w(broskerinos)) + @ldap.expect(:group_exists?, false, %w[broskerinos]) @fluff.ldap = @ldap refute(@fluff.valid_group?('broskerinos')) @ldap.verify @@ -56,7 +58,4 @@ def test_invalid_user refute(@fluff.valid_user?('johnny rotten')) @ldap.verify end - end - - diff --git a/test/lib/ldap_test_helper.rb b/test/ldap_test_helper.rb similarity index 56% rename from test/lib/ldap_test_helper.rb rename to test/ldap_test_helper.rb index 18f10e2..262ed9a 100644 --- a/test/lib/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -1,22 +1,22 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'ldap_fluff' -require 'ostruct' -require 'net/ldap' require 'minitest/autorun' module LdapTestHelper - attr_accessor :group_base, :class_filter, :user - def config_hash - { :host => "internet.com", - :port => "387", - :encryption => :start_tls, - :base_dn => "dc=internet,dc=com", - :group_base => "ou=group,dc=internet,dc=com", - :service_user => "service", - :service_pass => "pass", - :server_type => :free_ipa, - :attr_login => nil, - :search_filter => nil + { + host: 'internet.com', + port: 387, + encryption: :start_tls, + base_dn: 'dc=internet,dc=com', + group_base: 'ou=group,dc=internet,dc=com', + service_user: 'service', + service_pass: 'pass', + server_type: :free_ipa, + attr_login: nil, + search_filter: nil } end @@ -30,7 +30,7 @@ def config end def netgroups_config - @config ||= LdapFluff::Config.new config_hash.merge(:use_netgroups => true) + @config ||= LdapFluff::Config.new config_hash.merge(use_netgroups: true) end def service_bind @@ -40,38 +40,38 @@ def service_bind def basic_user @md = MiniTest::Mock.new - @md.expect(:find_user_groups, %w(bros), %w(john)) + @md.expect(:find_user_groups, %w[bros], %w[john]) get_test_instance_variable.member_service = @md end def bigtime_user @md = MiniTest::Mock.new - @md.expect(:find_user_groups, %w(bros broskies), %w(john)) + @md.expect(:find_user_groups, %w[bros broskies], %w[john]) get_test_instance_variable.member_service = @md end def ad_name_filter(name) - Net::LDAP::Filter.eq("samaccountname", name) + Net::LDAP::Filter.eq('samaccountname', name) end def ad_group_filter(name) - Net::LDAP::Filter.eq("cn", name) + Net::LDAP::Filter.eq('cn', name) end def ipa_name_filter(name) - Net::LDAP::Filter.eq("uid", name) + Net::LDAP::Filter.eq('uid', name) end def ipa_group_filter(name) - Net::LDAP::Filter.eq("cn", name) + Net::LDAP::Filter.eq('cn', name) end def group_filter(g) - Net::LDAP::Filter.eq("cn", g) + Net::LDAP::Filter.eq('cn', g) end def group_class_filter - Net::LDAP::Filter.eq("objectclass", "group") + Net::LDAP::Filter.eq('objectclass', 'group') end def ipa_user_bind(uid) @@ -87,31 +87,31 @@ def ad_group_dn(name='group') end def ad_user_payload - [{ :memberof => [ad_group_dn] }] + [{ memberof: [ad_group_dn] }] end def ad_group_payload - [{ :cn => "group", :memberof => [ad_group_dn] }] + [{ cn: 'group', memberof: [ad_group_dn] }] end def ad_parent_payload(num) - [{ :memberof => [ad_group_dn("bros#{num}")] }] + [{ memberof: [ad_group_dn("bros#{num}")] }] end def ad_double_payload(num) - [{ :memberof => [ad_group_dn("bros#{num}"), ad_group_dn("broskies#{num}")] }] + [{ memberof: [ad_group_dn("bros#{num}"), ad_group_dn("broskies#{num}")] }] end def posix_user_payload - [{ :cn => ["john"] }] + [{ cn: ['john'] }] end def posix_group_payload - [{ :cn => ["broze"] }] + [{ cn: ['broze'] }] end def posix_netgroup_payload(cn, netgroups=[]) - [{ :cn => [cn], :nisnetgrouptriple => netgroups }] + [{ cn: [cn], nisnetgrouptriple: netgroups }] end def ipa_user_payload @@ -125,11 +125,11 @@ def ipa_user_payload end def ipa_group_payload - [{ :cn => 'group' }, { :memberof => ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] }] + [{ cn: 'group' }, { memberof: ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] }] end def ipa_netgroup_payload(cn, netgroups=[]) - [{ :cn => [cn], :nisnetgrouptriple => netgroups }] + [{ cn: [cn], nisnetgrouptriple: netgroups }] end private diff --git a/test/posix_member_services_test.rb b/test/posix_member_services_test.rb index ce41e24..a1581db 100644 --- a/test/posix_member_services_test.rb +++ b/test/posix_member_services_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestPosixMemberService < MiniTest::Test include LdapTestHelper @@ -10,8 +12,8 @@ def setup def test_find_user user = posix_user_payload - @ldap.expect(:search, user, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, user, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert_equal posix_user_payload, @ms.find_user('john') @ldap.verify @@ -19,16 +21,16 @@ def test_find_user def test_find_user_groups user = posix_group_payload - @ldap.expect(:search, user, [:filter => @ms.name_filter('john'), - :base => config.group_base]) + @ldap.expect(:search, user, [filter: @ms.name_filter('john'), + base: config.group_base]) @ms.ldap = @ldap assert_equal ['broze'], @ms.find_user_groups('john') @ldap.verify end def test_find_no_groups - @ldap.expect(:search, [], [:filter => @ms.name_filter("john"), - :base => config.group_base]) + @ldap.expect(:search, [], [filter: @ms.name_filter('john'), + base: config.group_base]) @ms.ldap = @ldap assert_equal [], @ms.find_user_groups('john') @ldap.verify @@ -36,16 +38,16 @@ def test_find_no_groups def test_user_exists user = posix_user_payload - @ldap.expect(:search, user, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, user, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert @ms.find_user('john') @ldap.verify end def test_user_doesnt_exists - @ldap.expect(:search, nil, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, nil, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } @ldap.verify @@ -53,16 +55,16 @@ def test_user_doesnt_exists def test_group_exists group = posix_group_payload - @ldap.expect(:search, group, [:filter => @ms.group_filter('broze'), - :base => config.group_base]) + @ldap.expect(:search, group, [filter: @ms.group_filter('broze'), + base: config.group_base]) @ms.ldap = @ldap assert @ms.find_group('broze') @ldap.verify end def test_group_doesnt_exists - @ldap.expect(:search, nil, [:filter => @ms.group_filter('broze'), - :base => config.group_base]) + @ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), + base: config.group_base]) @ms.ldap = @ldap assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } @ldap.verify diff --git a/test/posix_netgroup_member_services_test.rb b/test/posix_netgroup_member_services_test.rb index 2e51f69..a767f2f 100644 --- a/test/posix_netgroup_member_services_test.rb +++ b/test/posix_netgroup_member_services_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestPosixNetgroupMemberService < MiniTest::Test include LdapTestHelper @@ -11,8 +13,8 @@ def setup def test_find_user user = posix_user_payload - @ldap.expect(:search, user, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, user, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert_equal posix_user_payload, @ms.find_user('john') @ldap.verify @@ -20,8 +22,8 @@ def test_find_user def test_find_user_groups response = posix_netgroup_payload('bros', ['(,john,)', '(,joe,)']) - @ldap.expect(:search, response, [:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - :base => config.group_base]) + @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), + base: config.group_base]) @ms.ldap = @ldap assert_equal ['bros'], @ms.find_user_groups('john') @@ -30,8 +32,8 @@ def test_find_user_groups def test_find_no_user_groups response = posix_netgroup_payload('bros', ['(,joe,)']) - @ldap.expect(:search, response, [:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - :base => config.group_base]) + @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), + base: config.group_base]) @ms.ldap = @ldap assert_equal [], @ms.find_user_groups('john') @@ -40,16 +42,16 @@ def test_find_no_user_groups def test_user_exists user = posix_user_payload - @ldap.expect(:search, user, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, user, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert @ms.find_user('john') @ldap.verify end def test_user_doesnt_exists - @ldap.expect(:search, nil, [:filter => @ms.name_filter('john'), - :base => config.base_dn]) + @ldap.expect(:search, nil, [filter: @ms.name_filter('john'), + base: config.base_dn]) @ms.ldap = @ldap assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } @ldap.verify @@ -57,16 +59,16 @@ def test_user_doesnt_exists def test_group_exists group = posix_netgroup_payload('broze') - @ldap.expect(:search, group, [:filter => @ms.group_filter('broze'), - :base => config.group_base]) + @ldap.expect(:search, group, [filter: @ms.group_filter('broze'), + base: config.group_base]) @ms.ldap = @ldap assert @ms.find_group('broze') @ldap.verify end def test_group_doesnt_exists - @ldap.expect(:search, nil, [:filter => @ms.group_filter('broze'), - :base => config.group_base]) + @ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), + base: config.group_base]) @ms.ldap = @ldap assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } @ldap.verify diff --git a/test/posix_test.rb b/test/posix_test.rb index 74dc592..7dfc356 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -1,4 +1,6 @@ -require 'lib/ldap_test_helper' +# frozen_string_literal: true + +require 'ldap_test_helper' class TestPosix < MiniTest::Test include LdapTestHelper @@ -16,12 +18,12 @@ def service_bind def test_groups service_bind basic_user - assert_equal(@posix.groups_for_uid("john"), %w(bros)) + assert_equal(@posix.groups_for_uid('john'), %w[bros]) end def test_missing_user md = MiniTest::Mock.new - md.expect(:find_user_groups, [], %w(john)) + md.expect(:find_user_groups, [], %w[john]) @posix.member_service = md assert_equal([], @posix.groups_for_uid('john')) end @@ -29,13 +31,13 @@ def test_missing_user def test_isnt_in_groups service_bind basic_user - assert_equal(@posix.is_in_groups('john', %w(broskies), true), false) + assert_equal(@posix.is_in_groups('john', %w[broskies], true), false) end def test_is_in_groups service_bind basic_user - assert_equal(@posix.is_in_groups('john', %w(bros), true), true) + assert_equal(@posix.is_in_groups('john', %w[bros], true), true) end def test_is_in_no_groups @@ -49,13 +51,13 @@ def test_good_bind @md = MiniTest::Mock.new user_result = MiniTest::Mock.new user_result.expect(:dn, 'uid=internet,dn=example') - @md.expect(:find_user, [user_result], %w(internet)) + @md.expect(:find_user, [user_result], %w[internet]) @posix.member_service = @md service_bind @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) @ldap.expect(:bind, true) @posix.ldap = @ldap - assert_equal(@posix.bind?("internet", "password"), true) + assert_equal(@posix.bind?('internet', 'password'), true) end def test_good_bind_with_dn @@ -63,20 +65,20 @@ def test_good_bind_with_dn @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) @ldap.expect(:bind, true) @posix.ldap = @ldap - assert_equal(@posix.bind?("uid=internet,dn=example", "password"), true) + assert_equal(@posix.bind?('uid=internet,dn=example', 'password'), true) end def test_bad_bind @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) @ldap.expect(:bind, false) @posix.ldap = @ldap - assert_equal(@posix.bind?("uid=internet,dn=example", "password"), false) + assert_equal(@posix.bind?('uid=internet,dn=example', 'password'), false) end def test_user_exists service_bind md = MiniTest::Mock.new - md.expect(:find_user, 'notnilluser', %w(john)) + md.expect(:find_user, 'notnilluser', %w[john]) @posix.member_service = md assert(@posix.user_exists?('john')) end @@ -84,7 +86,7 @@ def test_user_exists def test_missing_user service_bind md = MiniTest::Mock.new - md.expect(:find_user, nil, %w(john)) + md.expect(:find_user, nil, %w[john]) def md.find_user(uid) raise LdapFluff::Posix::MemberService::UIDNotFoundException end @@ -95,7 +97,7 @@ def md.find_user(uid) def test_group_exists service_bind md = MiniTest::Mock.new - md.expect(:find_group, 'notnillgroup', %w(broskies)) + md.expect(:find_group, 'notnillgroup', %w[broskies]) @posix.member_service = md assert(@posix.group_exists?('broskies')) end @@ -103,7 +105,7 @@ def test_group_exists def test_missing_group service_bind md = MiniTest::Mock.new - md.expect(:find_group, nil, %w(broskies)) + md.expect(:find_group, nil, %w[broskies]) def md.find_group(uid) raise LdapFluff::Posix::MemberService::GIDNotFoundException end @@ -120,8 +122,8 @@ def test_find_users_in_nested_groups @ldap.expect(:search, [nested_group], - [{ :base => group.dn, - :filter => Net::LDAP::Filter.eq('objectClass', 'posixGroup') | + [{ base: group.dn, + filter: Net::LDAP::Filter.eq('objectClass', 'posixGroup') | Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | Net::LDAP::Filter.eq('objectClass', 'groupOfNames')}]) From 7fac9928f3cc07ffae6585b7a2133e2b0fce3cf5 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 19:25:22 +0700 Subject: [PATCH 09/15] - Reformat all source-code and unit-tests - Remove unnecessary `active_support` from dependencies --- ldap_fluff.gemspec | 1 - lib/ldap_fluff/active_directory.rb | 15 +-- lib/ldap_fluff/ad_member_service.rb | 31 ++--- lib/ldap_fluff/config.rb | 112 ++++++++++++------ lib/ldap_fluff/error.rb | 3 +- lib/ldap_fluff/freeipa.rb | 5 +- lib/ldap_fluff/freeipa_member_service.rb | 23 ++-- .../freeipa_netgroup_member_service.rb | 9 +- lib/ldap_fluff/generic.rb | 32 +++-- lib/ldap_fluff/generic_member_service.rb | 45 ++++--- lib/ldap_fluff/posix.rb | 21 ++-- lib/ldap_fluff/posix_member_service.rb | 26 ++-- .../posix_netgroup_member_service.rb | 12 +- test/ad_member_services_test.rb | 4 +- test/ad_test.rb | 47 ++++---- test/ipa_member_services_test.rb | 2 +- test/ipa_test.rb | 15 +-- test/ldap_test_helper.rb | 21 ++-- test/posix_test.rb | 27 +++-- 19 files changed, 269 insertions(+), 182 deletions(-) diff --git a/ldap_fluff.gemspec b/ldap_fluff.gemspec index 1b9c5fe..93e2b32 100644 --- a/ldap_fluff.gemspec +++ b/ldap_fluff.gemspec @@ -21,7 +21,6 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.9.3' s.add_dependency 'net-ldap', '~> 0.12' - s.add_dependency 'activesupport' s.add_development_dependency 'bundler', '>= 1.14' s.add_development_dependency 'rake', '>= 10.0' diff --git a/lib/ldap_fluff/active_directory.rb b/lib/ldap_fluff/active_directory.rb index 999b8f3..bb174d9 100644 --- a/lib/ldap_fluff/active_directory.rb +++ b/lib/ldap_fluff/active_directory.rb @@ -1,5 +1,6 @@ -class LdapFluff::ActiveDirectory < LdapFluff::Generic +# frozen_string_literal: true +class LdapFluff::ActiveDirectory < LdapFluff::Generic def bind?(uid = nil, password = nil, opts = {}) unless uid.include?(',') || uid.include?('\\') || opts[:search] == false service_bind @@ -14,13 +15,14 @@ def bind?(uid = nil, password = nil, opts = {}) # TODO: query by group individually not like this def is_in_groups(uid, gids = [], all = false) service_bind - return true if gids == [] + return true if !gids || gids.empty? + begin groups = @member_service.find_user_groups(uid) intersection = gids & groups - return (all ? intersection == gids : intersection.size > 0) + all ? (intersection == gids) : !intersection.empty? rescue MemberService::UIDNotFoundException - return false + false end end @@ -37,14 +39,13 @@ def users_from_search_results(search, method) end objectclasses = entry.objectclass.map(&:downcase) - if (%w(organizationalperson person userproxy) & objectclasses).present? + if !(%w[organizationalperson person userproxy] & objectclasses).empty? users << @member_service.get_login_from_entry(entry) - elsif (%w(organizationalunit group) & objectclasses).present? + elsif !(%w[organizationalunit group] & objectclasses).empty? users << users_for_gid(entry.cn.first) end end users.flatten.uniq end - end diff --git a/lib/ldap_fluff/ad_member_service.rb b/lib/ldap_fluff/ad_member_service.rb index ae1f42c..ba8dcfc 100644 --- a/lib/ldap_fluff/ad_member_service.rb +++ b/lib/ldap_fluff/ad_member_service.rb @@ -1,14 +1,15 @@ -require 'net/ldap' +# frozen_string_literal: true -# Naughty bits of active directory ldap queries +# Naughty bits of active directory LDAP queries class LdapFluff::ActiveDirectory::MemberService < LdapFluff::GenericMemberService - + # @param [Net::LDAP] ldap + # @param [Config] config def initialize(ldap, config) @attr_login = (config.attr_login || 'samaccountname') super end - # get a list [] of ldap groups for a given user + # get a list [] of LDAP groups for a given user # in active directory, this means a recursive lookup def find_user_groups(uid) data = find_user(uid) @@ -19,9 +20,9 @@ def find_user_groups(uid) def _groups_from_ldap_data(payload) data = [] if !payload.nil? - first_level = payload[:memberof] - total_groups, _ = _walk_group_ancestry(first_level, first_level) - data = (get_groups(first_level + total_groups)).uniq + first_level = payload[:memberof] + total_groups, = _walk_group_ancestry(first_level, first_level) + data = (get_groups(first_level + total_groups)).uniq end data end @@ -30,21 +31,21 @@ def _groups_from_ldap_data(payload) def _walk_group_ancestry(group_dns = [], known_groups = []) set = [] group_dns.each do |group_dn| - search = @ldap.search(:base => group_dn, :scope => Net::LDAP::SearchScope_BaseObject, :attributes => ['memberof']) + search = @ldap.search(base: group_dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: ['memberof']) if !search.nil? && !search.first.nil? - groups = search.first[:memberof] - known_groups - known_groups += groups - next_level, new_known_groups = _walk_group_ancestry(groups, known_groups) - set += next_level - set += groups - known_groups += next_level + groups = search.first[:memberof] - known_groups + known_groups += groups + next_level, = _walk_group_ancestry(groups, known_groups) + set += next_level + set += groups + known_groups += next_level end end [set, known_groups] end def class_filter - Net::LDAP::Filter.eq("objectclass", "group") + Net::LDAP::Filter.eq('objectclass', 'group') end class UIDNotFoundException < LdapFluff::Error diff --git a/lib/ldap_fluff/config.rb b/lib/ldap_fluff/config.rb index 92517a6..d70bfd9 100644 --- a/lib/ldap_fluff/config.rb +++ b/lib/ldap_fluff/config.rb @@ -1,25 +1,58 @@ -require 'yaml' -require 'active_support/core_ext/hash' +# frozen_string_literal: true class LdapFluff::Config - ATTRIBUTES = %w[host port encryption base_dn group_base server_type service_user - service_pass anon_queries attr_login search_filter - instrumentation_service use_netgroups] - ATTRIBUTES.each { |attr| attr_reader attr.to_sym } - - DEFAULT_CONFIG = { 'port' => 389, - 'encryption' => nil, - 'base_dn' => 'dc=company,dc=com', - 'group_base' => 'dc=company,dc=com', - 'server_type' => :free_ipa, - 'anon_queries' => false, - 'instrumentation_service' => nil, - 'use_netgroups' => false } + ATTRIBUTES = [ + :host, :port, :encryption, :base_dn, :group_base, :server_type, :service_user, + :service_pass, :anon_queries, :attr_login, :search_filter, + :instrumentation_service, :use_netgroups + ].freeze + + DEFAULT_CONFIG = { + port: 389, + encryption: nil, + base_dn: 'dc=company,dc=com', + group_base: 'dc=company,dc=com', + server_type: :free_ipa, + anon_queries: false, + instrumentation_service: nil, + use_netgroups: false + }.freeze + + # @!attribute [r] host + # @return [String] + # @!attribute [r] port + # @return [Integer] + # @!attribute [r] encryption + # @return [Symbol, Hash] + # @!attribute [r] base_dn + # @return [String] + # @!attribute [r] group_base + # @return [String] + # @!attribute [r] server_type + # @return [Symbol] + # @!attribute [r] service_user + # @return [String] + # @!attribute [r] service_pass + # @return [String] + # @!attribute [r] anon_queries + # @return [Boolean] + # @!attribute [r] attr_login + # @return [String] + # @!attribute [r] search_filter + # @return [String] + # @!attribute [r] instrumentation_service + # @return [#instrument] + # @!attribute [r] use_netgroups + # @return [Boolean] + attr_reader(*ATTRIBUTES) + # @param [#to_hash] config + # @raise [ArgumentError] if config is not a Hash + # @raise [ConfigError] if config contains invalid keys def initialize(config) raise ArgumentError unless config.respond_to?(:to_hash) - config = validate(convert(config)) + config = validate(convert(config)) ATTRIBUTES.each do |attr| instance_variable_set(:"@#{attr}", config[attr]) end @@ -28,49 +61,62 @@ def initialize(config) private # @param [#to_hash] config + # @return [Hash] def convert(config) - config.to_hash.with_indifferent_access.tap do |conf| - %w[encryption server_type method].each do |key| - conf[key] = conf[key].is_a?(Hash) ? convert(conf[key]) : conf[key].to_sym if conf[key] + Hash[ + config.to_hash.map do |key, val| + key = key.to_sym if key.respond_to?(:to_sym) + + if val && [:encryption, :server_type, :method].include?(key) + val = val.is_a?(Hash) ? convert(val) : val.to_sym + end + + [key, val] end - end + ] end + # @param [Hash] config def missing_keys?(config) missing_keys = ATTRIBUTES - config.keys raise ConfigError, "missing configuration for keys: #{missing_keys.join(',')}" unless missing_keys.empty? end + # @param [Hash] config def unknown_keys?(config) unknown_keys = config.keys - ATTRIBUTES raise ConfigError, "unknown configuration keys: #{unknown_keys.join(',')}" unless unknown_keys.empty? end + # @param [Hash] config def all_required_keys?(config) - %w[host port base_dn group_base server_type].all? do |key| - raise ConfigError, "config key #{key} has to be set, it was nil" if config[key].nil? + [:host, :port, :base_dn, :group_base, :server_type].each do |key| + raise ConfigError, "config key #{key} has to be set, it was nil" unless config[key] end - %w[service_user service_pass].all? do |key| - if !config['anon_queries'] && config[key].nil? - raise ConfigError, "config key #{key} has to be set, it was nil" - end + [:service_user, :service_pass].each do |key| + raise ConfigError, "config key #{key} has to be set, it was nil" unless config[:anon_queries] || config[key] end end + # @param [Hash] config def anon_queries_set?(config) - unless [false, true].include?(config['anon_queries']) - raise ConfigError, "config key anon_queries has to be true or false but was #{config['anon_queries']}" - end + return if [false, true].include?(config[:anon_queries]) + + raise ConfigError, "config key anon_queries has to be true or false but was #{config[:anon_queries]}" end + # @param [Hash] config def correct_server_type?(config) - unless [:posix, :active_directory, :free_ipa].include?(config['server_type']) - raise ConfigError, 'config key server_type has to be :active_directory, :posix, :free_ipa ' + - "but was #{config['server_type']}" - end + return if [:posix, :active_directory, :free_ipa].include?(config[:server_type]) + + raise ConfigError, + "config key server_type has to be :active_directory, :posix, :free_ipa but was #{config[:server_type]}" end + # @param [Hash] config + # @return [Hash] + # @raise [ConfigError] if config contains invalid keys def validate(config) config = DEFAULT_CONFIG.merge(config) diff --git a/lib/ldap_fluff/error.rb b/lib/ldap_fluff/error.rb index 27c054c..c983c5d 100644 --- a/lib/ldap_fluff/error.rb +++ b/lib/ldap_fluff/error.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true + class LdapFluff class Error < StandardError end end - diff --git a/lib/ldap_fluff/freeipa.rb b/lib/ldap_fluff/freeipa.rb index 05cf204..d330437 100644 --- a/lib/ldap_fluff/freeipa.rb +++ b/lib/ldap_fluff/freeipa.rb @@ -1,5 +1,6 @@ -class LdapFluff::FreeIPA < LdapFluff::Generic +# frozen_string_literal: true +class LdapFluff::FreeIPA < LdapFluff::Generic def bind?(uid = nil, password = nil, opts = {}) unless uid.include?(',') unless opts[:search] == false @@ -16,7 +17,7 @@ def groups_for_uid(uid) begin super rescue MemberService::InsufficientQueryPrivilegesException - raise UnauthenticatedException, "Insufficient Privileges to query groups data" + raise UnauthenticatedException, 'Insufficient Privileges to query groups data' end end diff --git a/lib/ldap_fluff/freeipa_member_service.rb b/lib/ldap_fluff/freeipa_member_service.rb index 4dd2a6a..e870d4b 100644 --- a/lib/ldap_fluff/freeipa_member_service.rb +++ b/lib/ldap_fluff/freeipa_member_service.rb @@ -1,30 +1,33 @@ -require 'net/ldap' +# frozen_string_literal: true class LdapFluff::FreeIPA::MemberService < LdapFluff::GenericMemberService - + # @param [Net::LDAP] ldap + # @param [Config] config def initialize(ldap, config) @attr_login = (config.attr_login || 'uid') super end - # return an ldap user with groups attached - # note : this method is not particularly fast for large ldap systems + # @param [String] uid + # @return [Array] an LDAP user with groups attached + # @note this method is not particularly fast for large LDAP systems def find_user_groups(uid) user = find_user(uid) - # if group data is missing, they aren't querying with a user - # with enough privileges + + # if group data is missing, they aren't querying with a user with enough privileges user.delete_if { |u| u.nil? || !u.respond_to?(:attribute_names) || !u.attribute_names.include?(:memberof) } raise InsufficientQueryPrivilegesException if user.size < 1 + get_groups(user[0][:memberof]) end # extract the group names from the LDAP style response, - # return string will be something like - # CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com + # @param [Array] grouplist + # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) grouplist.map(&:downcase).collect do |g| if g.match(/.*?ipauniqueid=(.*?)/) - @ldap.search(:base => g)[0][:cn][0] + @ldap.search(base: g)[0][:cn][0] else g.sub(/.*?cn=(.*?),.*/, '\1') end @@ -39,6 +42,4 @@ class GIDNotFoundException < LdapFluff::Error class InsufficientQueryPrivilegesException < LdapFluff::Error end - end - diff --git a/lib/ldap_fluff/freeipa_netgroup_member_service.rb b/lib/ldap_fluff/freeipa_netgroup_member_service.rb index 7438ba0..0221291 100644 --- a/lib/ldap_fluff/freeipa_netgroup_member_service.rb +++ b/lib/ldap_fluff/freeipa_netgroup_member_service.rb @@ -1,14 +1,15 @@ -require 'net/ldap' +# frozen_string_literal: true class LdapFluff::FreeIPA::NetgroupMemberService < LdapFluff::FreeIPA::MemberService - + # @param [String] uid + # @return [Array] def find_user_groups(uid) groups = [] - @ldap.search(:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), :base => @group_base).each do |entry| + @ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: @group_base).each do |entry| members = get_netgroup_users(entry[:nisnetgrouptriple]) groups << entry[:cn][0] if members.include? uid end + groups end end - diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index 2cef378..ac74b44 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -1,12 +1,22 @@ +# frozen_string_literal: true + class LdapFluff::Generic + # @!attribute [rw] ldap + # @return [Net::LDAP] + # @!attribute [rw] member_service + # @return [GenericMemberService] attr_accessor :ldap, :member_service - def initialize(config = {}) - @ldap = Net::LDAP.new(:host => config.host, - :base => config.base_dn, - :port => config.port, - :encryption => config.encryption, - :instrumentation_service => config.instrumentation_service) + # @param [Config] config + def initialize(config) + @ldap = Net::LDAP.new( + host: config.host, + base: config.base_dn, + port: config.port, + encryption: config.encryption, + instrumentation_service: config.instrumentation_service + ) + @bind_user = config.service_user @bind_pass = config.service_pass @anon = config.anon_queries @@ -17,6 +27,8 @@ def initialize(config = {}) @member_service = create_member_service(config) end + # @param [String] uid + # @return [Boolean] def user_exists?(uid) service_bind @member_service.find_user(uid) @@ -68,17 +80,20 @@ def is_in_groups(uid, gids = [], all = true) def includes_cn?(cn) filter = Net::LDAP::Filter.eq('cn', cn) - @ldap.search(:base => @ldap.base, :filter => filter).present? + results = @ldap.search(base: @ldap.base, filter: filter) + # NOTE: present? + !(results.respond_to?(:empty?) ? results.empty? : !results) end def service_bind - unless @anon || bind?(@bind_user, @bind_pass, :search => false) + unless @anon || bind?(@bind_user, @bind_pass, search: false) raise UnauthenticatedException, "Could not bind to #{class_name} user #{@bind_user}" end end private + def select_member_method(search_result) if @use_netgroups :nisnetgrouptriple @@ -114,4 +129,3 @@ def users_from_search_results(search, method) class UnauthenticatedException < LdapFluff::Error end end - diff --git a/lib/ldap_fluff/generic_member_service.rb b/lib/ldap_fluff/generic_member_service.rb index 78c4cdb..9cfecf1 100644 --- a/lib/ldap_fluff/generic_member_service.rb +++ b/lib/ldap_fluff/generic_member_service.rb @@ -1,39 +1,54 @@ -require 'net/ldap' +# frozen_string_literal: true class LdapFluff::GenericMemberService - + # @return [Net::LDAP] attr_accessor :ldap + # @param [Net::LDAP] ldap + # @param [Config] config def initialize(ldap, config) @ldap = ldap @base = config.base_dn @group_base = (config.group_base.empty? ? config.base_dn : config.group_base) + @search_filter = nil begin - @search_filter = Net::LDAP::Filter.construct(config.search_filter) unless (config.search_filter.nil? || config.search_filter.empty?) - rescue Net::LDAP::LdapError => error - puts "Search filter unavailable - #{error}" + @search_filter = Net::LDAP::Filter.construct(config.search_filter) unless + !config.search_filter || config.search_filter.empty? + rescue Net::LDAP::LdapError => e + puts "Search filter unavailable - #{e}" end end + # @param [String] uid + # @return [Array, Net::LDAP::Entry] + # @raise [UIDNotFoundException] def find_user(uid) - user = @ldap.search(:filter => name_filter(uid)) + user = @ldap.search(filter: name_filter(uid)) raise self.class::UIDNotFoundException if (user.nil? || user.empty?) + user end + # @param [String] dn + # @return [Array, Net::LDAP::Entry] + # @raise [UIDNotFoundException] def find_by_dn(dn) + # @type [String] entry entry, base = dn.split(/(? name_filter(entry_value, entry_attr), :base => base) - raise self.class::UIDNotFoundException if (user.nil? || user.empty?) + + user = @ldap.search(filter: name_filter(entry_value, entry_attr), base: base) + raise self.class::UIDNotFoundException if !user || user.empty? + user end def find_group(gid) - group = @ldap.search(:filter => group_filter(gid), :base => @group_base) - raise self.class::GIDNotFoundException if (group.nil? || group.empty?) + group = @ldap.search(filter: group_filter(gid), base: @group_base) + raise self.class::GIDNotFoundException if !group || group.empty? + group end @@ -48,25 +63,26 @@ def name_filter(uid, attr = @attr_login) end def group_filter(gid) - Net::LDAP::Filter.eq("cn", gid) + Net::LDAP::Filter.eq('cn', gid) end # extract the group names from the LDAP style response, # return string will be something like # CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) - grouplist.map(&:downcase).collect { |g| g.sub(/.*?cn=(.*?),.*/, '\1') } + grouplist.map { |g| g.downcase.sub(/.*?cn=(.*?),.*/, '\1') } end def get_netgroup_users(netgroup_triples) - return [] if netgroup_triples.nil? + return [] unless netgroup_triples + netgroup_triples.map { |m| m.split(',')[1] } end def get_logins(userlist) userlist.map(&:downcase!) [@attr_login, 'uid', 'cn'].map do |attribute| - logins = userlist.collect { |g| g.sub(/.*?#{attribute}=(.*?),.*/, '\1') } + logins = userlist.map { |g| g.sub(/.*?#{attribute}=(.*?),.*/, '\1') } if logins == userlist nil else @@ -81,5 +97,4 @@ def get_login_from_entry(entry) end nil end - end diff --git a/lib/ldap_fluff/posix.rb b/lib/ldap_fluff/posix.rb index 84612f7..ce0cadd 100644 --- a/lib/ldap_fluff/posix.rb +++ b/lib/ldap_fluff/posix.rb @@ -1,5 +1,6 @@ -class LdapFluff::Posix < LdapFluff::Generic +# frozen_string_literal: true +class LdapFluff::Posix < LdapFluff::Generic def bind?(uid = nil, password = nil, opts = {}) unless uid.include?(',') || opts[:search] == false service_bind @@ -12,20 +13,20 @@ def bind?(uid = nil, password = nil, opts = {}) private + # To find groups in standard LDAP without group membership attributes + # we have to look for OUs or posixGroups within the current group scope, + # i.e: cn=ldapusers,ou=groups,dc=example,dc=com -> cn=myusers,cn=ldapusers,ou=gr... def users_from_search_results(search, method) - # To find groups in standard LDAP without group membership attributes - # we have to look for OUs or posixGroups within the current group scope, - # i.e: cn=ldapusers,ou=groups,dc=example,dc=com -> cn=myusers,cn=ldapusers,ou=gr... - if @use_netgroups filter = Net::LDAP::Filter.eq('objectClass', 'nisNetgroup') else - filter = Net::LDAP::Filter.eq('objectClass','posixGroup') | - Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | - Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | - Net::LDAP::Filter.eq('objectClass', 'groupOfNames') + filter = + Net::LDAP::Filter.eq('objectClass', 'posixGroup') | + Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | + Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | + Net::LDAP::Filter.eq('objectClass', 'groupOfNames') end - groups = @ldap.search(:base => search.dn, :filter => filter) + groups = @ldap.search(base: search.dn, filter: filter) members = groups.map { |group| group.send(method) }.flatten.uniq if method == :memberuid diff --git a/lib/ldap_fluff/posix_member_service.rb b/lib/ldap_fluff/posix_member_service.rb index e699b52..2b33e26 100644 --- a/lib/ldap_fluff/posix_member_service.rb +++ b/lib/ldap_fluff/posix_member_service.rb @@ -1,29 +1,38 @@ -require 'net/ldap' +# frozen_string_literal: true -# handles the naughty bits of posix ldap +# handles the naughty bits of POSIX LDAP class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService - + # @param [Net::LDAP] ldap + # @param [Config] config def initialize(ldap, config) @attr_login = (config.attr_login || 'memberuid') super end + # @param [String] uid + # @return [Array, Net::LDAP::Entry] + # @raise [UIDNotFoundException] def find_user(uid, base_dn = @base) - user = @ldap.search(:filter => name_filter(uid), :base => base_dn) + user = @ldap.search(filter: name_filter(uid), base: base_dn) raise UIDNotFoundException if (user.nil? || user.empty?) + user end - # return an ldap user with groups attached - # note : this method is not particularly fast for large ldap systems + # @param [String] uid + # @return [Array] an LDAP user with groups attached + # @note this method is not particularly fast for large LDAP systems def find_user_groups(uid) groups = [] - @ldap.search(:filter => Net::LDAP::Filter.eq('memberuid', uid), :base => @group_base).each do |entry| + @ldap.search(filter: Net::LDAP::Filter.eq('memberuid', uid), base: @group_base).each do |entry| groups << entry[:cn][0] end groups end + # @param [String] uid + # @param [Array] gids + # @deprecated def times_in_groups(uid, gids, all) filters = [] gids.each do |cn| @@ -31,7 +40,7 @@ def times_in_groups(uid, gids, all) end group_filters = merge_filters(filters, all) filter = name_filter(uid) & group_filters - @ldap.search(:base => @group_base, :filter => filter).size + @ldap.search(base: @group_base, filter: filter).size end # AND or OR all of the filters together @@ -50,5 +59,4 @@ class UIDNotFoundException < LdapFluff::Error class GIDNotFoundException < LdapFluff::Error end - end diff --git a/lib/ldap_fluff/posix_netgroup_member_service.rb b/lib/ldap_fluff/posix_netgroup_member_service.rb index 2065279..f0c07fb 100644 --- a/lib/ldap_fluff/posix_netgroup_member_service.rb +++ b/lib/ldap_fluff/posix_netgroup_member_service.rb @@ -1,16 +1,16 @@ -require 'net/ldap' +# frozen_string_literal: true -# handles the naughty bits of posix ldap +# handles the naughty bits of POSIX LDAP class LdapFluff::Posix::NetgroupMemberService < LdapFluff::Posix::MemberService - - # return list of group CNs for a user + # @param [String] uid + # @return [Array] list of group CNs for a user def find_user_groups(uid) groups = [] - @ldap.search(:filter => Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), :base => @group_base).each do |entry| + @ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: @group_base).each do |entry| members = get_netgroup_users(entry[:nisnetgrouptriple]) groups << entry[:cn][0] if members.include? uid end + groups end - end diff --git a/test/ad_member_services_test.rb b/test/ad_member_services_test.rb index e9f21b5..bcbfc8c 100644 --- a/test/ad_member_services_test.rb +++ b/test/ad_member_services_test.rb @@ -7,7 +7,7 @@ class TestADMemberService < MiniTest::Test def setup super - @adms = LdapFluff::ActiveDirectory::MemberService.new(@ldap, @config) + @adms = LdapFluff::ActiveDirectory::MemberService.new(@ldap, @config) @gfilter = group_filter('group') & group_class_filter end @@ -22,7 +22,7 @@ def basic_group def nest_deep(n) # add all the expects - 1.upto(n-1) do |i| + 1.upto(n - 1) do |i| @ldap.expect(:search, ad_parent_payload(i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) end # terminate or we loop FOREVER diff --git a/test/ad_test.rb b/test/ad_test.rb index 12cd6db..7e2bb8c 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -7,7 +7,7 @@ class TestAD < MiniTest::Test def setup super - @ad = LdapFluff::ActiveDirectory.new(@config) + @ad = LdapFluff::ActiveDirectory.new(@config) end # default setup for service bind users @@ -18,7 +18,7 @@ def service_bind def test_good_bind # no expectation on the service account - @ldap.expect(:auth, nil, ['EXAMPLE\\internet', 'password']) + @ldap.expect(:auth, nil, %w[EXAMPLE\\internet password]) @ldap.expect(:bind, true) @ad.ldap = @ldap assert_equal(@ad.bind?('EXAMPLE\\internet', 'password'), true) @@ -65,9 +65,8 @@ def test_groups def test_bad_user service_bind md = MiniTest::Mock.new - md.expect(:find_user_groups, nil, %w[john]) - def md.find_user_groups(*args) - raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException + md.expect(:find_user_groups, nil) do |uid| + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' end @ad.member_service = md assert_equal(@ad.groups_for_uid('john'), []) @@ -113,14 +112,14 @@ def test_group_subset end def test_subgroups_in_groups_are_ignored - group = Net::LDAP::Entry.new('foremaners') + group = Net::LDAP::Entry.new('foremaners') md = MiniTest::Mock.new 2.times { md.expect(:find_group, [group], ['foremaners']) } 2.times { service_bind } - def md.find_by_dn(dn) - raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException - end + + # TODO: md.expect(:find_by_dn, nil) { raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException } @ad.member_service = md + assert_equal @ad.users_for_gid('foremaners'), [] md.verify end @@ -135,9 +134,8 @@ def test_user_exists def test_missing_user md = MiniTest::Mock.new - md.expect(:find_user, nil, %w[john]) - def md.find_user(uid) - raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException + md.expect(:find_user, nil) do |uid| + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' end @ad.member_service = md service_bind @@ -154,9 +152,8 @@ def test_group_exists def test_missing_group md = MiniTest::Mock.new - md.expect(:find_group, nil, %w[broskies]) - def md.find_group(uid) - raise LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException + md.expect(:find_group, nil) do |gid| + raise LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException if gid == 'broskies' end @ad.member_service = md service_bind @@ -164,15 +161,15 @@ def md.find_group(uid) end def test_find_users_in_nested_groups - group = Net::LDAP::Entry.new('foremaners') + group = Net::LDAP::Entry.new('foremaners') nested_group = Net::LDAP::Entry.new('katellers') - nested_user = Net::LDAP::Entry.new('testuser') + nested_user = Net::LDAP::Entry.new('testuser') - group[:member] = ['CN=katellers,DC=corp,DC=windows,DC=com'] - nested_group[:cn] = ['katellers'] + group[:member] = ['CN=katellers,DC=corp,DC=windows,DC=com'] + nested_group[:cn] = ['katellers'] nested_group[:member] = ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com'] nested_group[:objectclass] = ['organizationalunit'] - nested_user[:objectclass] = ['person'] + nested_user[:objectclass] = ['person'] md = MiniTest::Mock.new 2.times { md.expect(:find_group, [group], ['foremaners']) } @@ -180,7 +177,7 @@ def test_find_users_in_nested_groups 2.times { service_bind } md.expect(:find_by_dn, [nested_group], ['CN=katellers,DC=corp,DC=windows,DC=com']) - md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) + md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) md.expect(:get_login_from_entry, 'testuser', [nested_user]) @ad.member_service = md assert_equal @ad.users_for_gid('foremaners'), ['testuser'] @@ -188,22 +185,22 @@ def test_find_users_in_nested_groups end def test_find_users_with_empty_nested_group - group = Net::LDAP::Entry.new('foremaners') + group = Net::LDAP::Entry.new('foremaners') nested_group = Net::LDAP::Entry.new('katellers') - nested_user = Net::LDAP::Entry.new('testuser') + nested_user = Net::LDAP::Entry.new('testuser') group[:member] = ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', 'CN=katellers,DC=corp,DC=windows,DC=com'] nested_group[:cn] = ['katellers'] nested_group[:objectclass] = ['organizationalunit'] nested_group[:memberof] = ['CN=foremaners,DC=corp,DC=windows,DC=com'] - nested_user[:objectclass] = ['person'] + nested_user[:objectclass] = ['person'] md = MiniTest::Mock.new 2.times { md.expect(:find_group, [group], ['foremaners']) } 2.times { md.expect(:find_group, [nested_group], ['katellers']) } 2.times { service_bind } - md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) + md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) md.expect(:find_by_dn, [nested_group], ['CN=katellers,DC=corp,DC=windows,DC=com']) md.expect(:get_login_from_entry, 'testuser', [nested_user]) @ad.member_service = md diff --git a/test/ipa_member_services_test.rb b/test/ipa_member_services_test.rb index 8d65033..3d72e4b 100644 --- a/test/ipa_member_services_test.rb +++ b/test/ipa_member_services_test.rb @@ -37,7 +37,7 @@ def test_missing_user def test_no_groups entry = Net::LDAP::Entry.new entry['memberof'] = [] - @ldap.expect(:search, [ Net::LDAP::Entry.new, entry ], [filter: ipa_name_filter('john')]) + @ldap.expect(:search, [Net::LDAP::Entry.new, entry], [filter: ipa_name_filter('john')]) @ipams.ldap = @ldap assert_equal([], @ipams.find_user_groups('john')) @ldap.verify diff --git a/test/ipa_test.rb b/test/ipa_test.rb index f26aea3..b90a20b 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -56,9 +56,8 @@ def test_groups def test_bad_user service_bind @md = MiniTest::Mock.new - @md.expect(:find_user_groups, nil, %w[john]) - def @md.find_user_groups(*args) - raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException + @md.expect(:find_user_groups, nil) do |uid| + raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' end @ipa.member_service = @md assert_equal(@ipa.groups_for_uid('john'), []) @@ -119,9 +118,8 @@ def test_user_exists def test_missing_user @md = MiniTest::Mock.new - @md.expect(:find_user, nil, %w[john]) - def @md.find_user(uid) - raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException + @md.expect(:find_user, nil) do |uid| + raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' end @ipa.member_service = @md service_bind @@ -138,9 +136,8 @@ def test_group_exists def test_missing_group @md = MiniTest::Mock.new - @md.expect(:find_group, nil, %w[broskies]) - def @md.find_group(uid) - raise LdapFluff::FreeIPA::MemberService::GIDNotFoundException + @md.expect(:find_group, nil) do |gid| + raise LdapFluff::FreeIPA::MemberService::GIDNotFoundException if gid == 'broskies' end @ipa.member_service = @md service_bind diff --git a/test/ldap_test_helper.rb b/test/ldap_test_helper.rb index 262ed9a..a105324 100644 --- a/test/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -2,6 +2,7 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'ldap_fluff' + require 'minitest/autorun' module LdapTestHelper @@ -82,7 +83,7 @@ def ad_user_dn(name) "CN=#{name},CN=Users,#{@config.base_dn}" end - def ad_group_dn(name='group') + def ad_group_dn(name = 'group') "cn=#{name},#{@config.group_base}" end @@ -110,17 +111,19 @@ def posix_group_payload [{ cn: ['broze'] }] end - def posix_netgroup_payload(cn, netgroups=[]) + def posix_netgroup_payload(cn, netgroups = []) [{ cn: [cn], nisnetgrouptriple: netgroups }] end def ipa_user_payload @ipa_user_payload_cache ||= begin - entry_1 = Net::LDAP::Entry.new - entry_1['cn'] = 'John' - entry_2 = Net::LDAP::Entry.new - entry_2['memberof'] = ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] - [ entry_1, entry_2 ] + entry1 = Net::LDAP::Entry.new + entry1['cn'] = 'John' + + entry2 = Net::LDAP::Entry.new + entry2['memberof'] = ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] + + [entry1, entry2] end end @@ -128,13 +131,13 @@ def ipa_group_payload [{ cn: 'group' }, { memberof: ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] }] end - def ipa_netgroup_payload(cn, netgroups=[]) + def ipa_netgroup_payload(cn, netgroups = []) [{ cn: [cn], nisnetgrouptriple: netgroups }] end private def get_test_instance_variable - instance_variable_get("@#{self.class.to_s.underscore.split('_')[1..-1].join}") + instance_variable_get("@#{self.class.name.sub(/^Test|Test$/, '').downcase}") end end diff --git a/test/posix_test.rb b/test/posix_test.rb index 7dfc356..44f600c 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -86,9 +86,8 @@ def test_user_exists def test_missing_user service_bind md = MiniTest::Mock.new - md.expect(:find_user, nil, %w[john]) - def md.find_user(uid) - raise LdapFluff::Posix::MemberService::UIDNotFoundException + md.expect(:find_user, nil) do |uid| + raise LdapFluff::Posix::MemberService::UIDNotFoundException if uid == 'john' end @posix.member_service = md refute(@posix.user_exists?('john')) @@ -105,9 +104,8 @@ def test_group_exists def test_missing_group service_bind md = MiniTest::Mock.new - md.expect(:find_group, nil, %w[broskies]) - def md.find_group(uid) - raise LdapFluff::Posix::MemberService::GIDNotFoundException + md.expect(:find_group, nil) do |gid| + raise LdapFluff::Posix::MemberService::GIDNotFoundException if gid == 'broskies' end @posix.member_service = md refute(@posix.group_exists?('broskies')) @@ -120,13 +118,7 @@ def test_find_users_in_nested_groups nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com') nested_group[:memberuid] = ['testuser'] - @ldap.expect(:search, - [nested_group], - [{ base: group.dn, - filter: Net::LDAP::Filter.eq('objectClass', 'posixGroup') | - Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | - Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | - Net::LDAP::Filter.eq('objectClass', 'groupOfNames')}]) + @ldap.expect(:search, [nested_group], [base: group.dn, filter: groups_filter]) @posix.ldap = @ldap md = MiniTest::Mock.new @@ -138,4 +130,13 @@ def test_find_users_in_nested_groups md.verify @ldap.verify end + + private + + def groups_filter + Net::LDAP::Filter.eq('objectClass', 'posixGroup') | + Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | + Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | + Net::LDAP::Filter.eq('objectClass', 'groupOfNames') + end end From 6deecd2ba99f8328891eb1dade931d05e40dd232 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Wed, 13 Nov 2019 23:19:18 +0700 Subject: [PATCH 10/15] Rename method `is_in_groups?` to `user_in_groups?` by `rubocop` recommendation --- README.rdoc | 2 +- lib/ldap_fluff.rb | 6 +++--- lib/ldap_fluff/active_directory.rb | 2 +- lib/ldap_fluff/generic.rb | 6 +++--- test/ad_test.rb | 10 +++++----- test/ipa_test.rb | 12 ++++++------ test/ldap_test.rb | 4 ++-- test/posix_test.rb | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.rdoc b/README.rdoc index 117f055..be95857 100644 --- a/README.rdoc +++ b/README.rdoc @@ -25,7 +25,7 @@ It exposes these methods: user_list(gid) returns the set of users that belong to an LDAP group - is_in_groups?(uid, grouplist) + user_in_groups?(uid, grouplist) returns true if the user provided is in all of the groups listed in grouplist valid_user?(uid) diff --git a/lib/ldap_fluff.rb b/lib/ldap_fluff.rb index 4fa74d2..6f73f80 100644 --- a/lib/ldap_fluff.rb +++ b/lib/ldap_fluff.rb @@ -77,9 +77,9 @@ def group_list(uid) # @param [String] uid # @return [Boolean] true if a user is in all of the groups in grouplist - def is_in_groups?(uid, grouplist) - instrument('is_in_groups?.ldap_fluff', uid: uid, grouplist: grouplist) do |payload| - @ldap.is_in_groups(uid, grouplist, true) + def user_in_groups?(uid, grouplist) + instrument('user_in_groups?.ldap_fluff', uid: uid, grouplist: grouplist) do |payload| + @ldap.user_in_groups?(uid, grouplist, true) end end diff --git a/lib/ldap_fluff/active_directory.rb b/lib/ldap_fluff/active_directory.rb index bb174d9..a77888e 100644 --- a/lib/ldap_fluff/active_directory.rb +++ b/lib/ldap_fluff/active_directory.rb @@ -13,7 +13,7 @@ def bind?(uid = nil, password = nil, opts = {}) # active directory stores group membership on a users model # TODO: query by group individually not like this - def is_in_groups(uid, gids = [], all = false) + def user_in_groups?(uid, gids = [], all = false) service_bind return true if !gids || gids.empty? diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index ac74b44..6af2f81 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -67,14 +67,14 @@ def users_for_gid(gid) # # returns true if owner is in ALL of the groups if all=true, otherwise # returns true if owner is in ANY of the groups - def is_in_groups(uid, gids = [], all = true) + def user_in_groups?(uid, gids = [], all = true) service_bind groups = @member_service.find_user_groups(uid).sort gids = gids.sort if all - return groups & gids == gids + groups & gids == gids else - return (groups & gids).any? + (groups & gids).any? end end diff --git a/test/ad_test.rb b/test/ad_test.rb index 7e2bb8c..105aad4 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -84,31 +84,31 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ad.is_in_groups('john', %w[bros], false), true) + assert_equal(@ad.user_in_groups?('john', %w[bros], false), true) end def test_is_some_groups service_bind basic_user - assert_equal(@ad.is_in_groups('john', %w[bros buds], false), true) + assert_equal(@ad.user_in_groups?('john', %w[bros buds], false), true) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ad.is_in_groups('john', %w[bros buds], true), false) + assert_equal(@ad.user_in_groups?('john', %w[bros buds], true), false) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ad.is_in_groups('john', %w[broskies], false), false) + assert_equal(@ad.user_in_groups?('john', %w[broskies], false), false) end def test_group_subset service_bind bigtime_user - assert_equal(@ad.is_in_groups('john', %w[broskies], true), true) + assert_equal(@ad.user_in_groups?('john', %w[broskies], true), true) end def test_subgroups_in_groups_are_ignored diff --git a/test/ipa_test.rb b/test/ipa_test.rb index b90a20b..cefd566 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -75,37 +75,37 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ipa.is_in_groups('john', %w[bros], false), true) + assert_equal(@ipa.user_in_groups?('john', %w[bros], false), true) end def test_is_some_groups service_bind basic_user - assert_equal(@ipa.is_in_groups('john', %w[bros buds], false), true) + assert_equal(@ipa.user_in_groups?('john', %w[bros buds], false), true) end def test_is_in_all_groupss service_bind bigtime_user - assert_equal(true, @ipa.is_in_groups('john', %w[broskies bros], true)) + assert_equal(true, @ipa.user_in_groups?('john', %w[broskies bros], true)) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ipa.is_in_groups('john', %w[bros buds], true), false) + assert_equal(@ipa.user_in_groups?('john', %w[bros buds], true), false) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ipa.is_in_groups('john', %w[broskies], false), false) + assert_equal(@ipa.user_in_groups?('john', %w[broskies], false), false) end def test_group_subset service_bind bigtime_user - assert_equal(@ipa.is_in_groups('john', %w[broskies], true), true) + assert_equal(@ipa.user_in_groups?('john', %w[broskies], true), true) end def test_user_exists diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 90a2fd3..65d3f02 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -25,9 +25,9 @@ def test_groups end def test_group_membership - @ldap.expect(:is_in_groups, false, ['john', %w[broskies girlfriends], true]) + @ldap.expect(:user_in_groups?, false, ['john', %w[broskies girlfriends], true]) @fluff.ldap = @ldap - assert_equal(@fluff.is_in_groups?('john', %w[broskies girlfriends]), false) + assert_equal(@fluff.user_in_groups?('john', %w[broskies girlfriends]), false) @ldap.verify end diff --git a/test/posix_test.rb b/test/posix_test.rb index 44f600c..8c526bb 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -31,19 +31,19 @@ def test_missing_user def test_isnt_in_groups service_bind basic_user - assert_equal(@posix.is_in_groups('john', %w[broskies], true), false) + assert_equal(@posix.user_in_groups?('john', %w[broskies], true), false) end def test_is_in_groups service_bind basic_user - assert_equal(@posix.is_in_groups('john', %w[bros], true), true) + assert_equal(@posix.user_in_groups?('john', %w[bros], true), true) end def test_is_in_no_groups service_bind basic_user - assert_equal(@posix.is_in_groups('john', [], true), true) + assert_equal(@posix.user_in_groups?('john', [], true), true) end def test_good_bind From 6c37f38cb63a3cfbd2f83734e9ecfbd128bbfe32 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Thu, 14 Nov 2019 09:54:57 +0700 Subject: [PATCH 11/15] - Refactor all source-code to apply almost `rubocop` rules - Remove unnecessary instance variables by using `config` property - Add optional parameter `only` into `find_*` methods to get one result - Refactor all unit-tests to apply almost `rubocop` rules --- README.rdoc | 10 +- lib/ldap_fluff.rb | 67 +++---- lib/ldap_fluff/active_directory.rb | 65 ++++--- lib/ldap_fluff/ad_member_service.rb | 66 ++++--- lib/ldap_fluff/config.rb | 21 +-- lib/ldap_fluff/freeipa.rb | 75 ++++---- lib/ldap_fluff/freeipa_member_service.rb | 17 +- .../freeipa_netgroup_member_service.rb | 11 +- lib/ldap_fluff/generic.rb | 125 +++++++------ lib/ldap_fluff/generic_member_service.rb | 118 ++++++++----- lib/ldap_fluff/posix.rb | 52 ++++-- lib/ldap_fluff/posix_member_service.rb | 51 +++--- .../posix_netgroup_member_service.rb | 11 +- test/ad_member_services_test.rb | 112 ++++++------ test/ad_test.rb | 167 +++++++++--------- test/config_test.rb | 18 +- test/ipa_member_services_test.rb | 41 +++-- test/ipa_netgroup_member_services_test.rb | 44 +++-- test/ipa_test.rb | 132 +++++++------- test/ldap_test.rb | 50 +++--- test/ldap_test_helper.rb | 134 +++++++------- test/posix_member_services_test.rb | 54 +++--- test/posix_netgroup_member_services_test.rb | 55 +++--- test/posix_test.rb | 113 ++++++------ 24 files changed, 871 insertions(+), 738 deletions(-) diff --git a/README.rdoc b/README.rdoc index be95857..89ba327 100644 --- a/README.rdoc +++ b/README.rdoc @@ -34,10 +34,10 @@ It exposes these methods: valid_group?(uid) returns true if the group provided exists - find_user(uid) + find_user(uid, only = nil) returns the LDAP entry of the user if found, nil if not found - find_group(gid) + find_group(gid, only = nil) returns the LDAP entry of the group if found, nil if not found These methods are handy for using LDAP for both authentication and authorization. @@ -49,12 +49,12 @@ Your global configuration must provide information about your LDAP host to funct host: # ip address or hostname port: # port encryption: # blank, :simple_tls, or :start_tls - base_dn: # base DN for LDAP auth, eg dc=redhat,dc=com + base_dn: # base DN for LDAP auth, eg dc=redhat,dc=com group_base: # base DN for your LDAP groups, eg ou=Groups,dc=redhat,dc=com use_netgroups: # false by default, use true if you want to use netgroup triples, # supported only for server type :free_ipa and :posix - server_type: # type of server. default == :posix. :active_directory, :posix, :free_ipa - ad_domain: # domain for your users if using active directory, eg redhat.com + server_type: # type of server. default == :posix. :active_directory, :posix, :free_ipa + ad_domain: # domain for your users if using active directory, eg redhat.com service_user: # service account for authenticating LDAP calls. required unless you enable anon service_pass: # service password for authenticating LDAP calls. required unless you enable anon anon_queries: # false by default, true if you don't want to use the service user diff --git a/lib/ldap_fluff.rb b/lib/ldap_fluff.rb index 6f73f80..d2d9103 100644 --- a/lib/ldap_fluff.rb +++ b/lib/ldap_fluff.rb @@ -23,20 +23,9 @@ class LdapFluff attr_accessor :ldap, :instrumentation_service def initialize(config = {}) - config = LdapFluff::Config.new(config) - - @ldap = - case config.server_type - when :posix - Posix.new(config) - when :active_directory - ActiveDirectory.new(config) - when :free_ipa - FreeIPA.new(config) - else - raise 'unknown server_type' - end + config = Config.new(config) + @ldap = create_provider(config) @instrumentation_service = config.instrumentation_service end @@ -45,17 +34,13 @@ def initialize(config = {}) # @return [Boolean] def authenticate?(uid, password) instrument('authenticate.ldap_fluff', uid: uid) do |payload| - if password.nil? || password.empty? - false - else - !!@ldap.bind?(uid, password) - end + !password || password.empty? ? false : ldap.bind?(payload[:uid], password) end end def test - instrument('test.ldap_fluff') do |payload| - @ldap.ldap.open {} + instrument('test.ldap_fluff') do # |payload| + ldap.ldap.open {} end end @@ -63,7 +48,7 @@ def test # @return [Array] a list of users for a given gid def user_list(gid) instrument('user_list.ldap_fluff', gid: gid) do |payload| - @ldap.users_for_gid(gid) + ldap.users_for_gid payload[:gid] end end @@ -71,7 +56,7 @@ def user_list(gid) # @return [Array] a list of groups for a given uid def group_list(uid) instrument('group_list.ldap_fluff', uid: uid) do |payload| - @ldap.groups_for_uid(uid) + ldap.groups_for_uid payload[:uid] end end @@ -79,7 +64,7 @@ def group_list(uid) # @return [Boolean] true if a user is in all of the groups in grouplist def user_in_groups?(uid, grouplist) instrument('user_in_groups?.ldap_fluff', uid: uid, grouplist: grouplist) do |payload| - @ldap.user_in_groups?(uid, grouplist, true) + ldap.user_in_groups? payload[:uid], payload[:grouplist], true end end @@ -87,7 +72,7 @@ def user_in_groups?(uid, grouplist) # @return [Boolean] true if uid exists def valid_user?(uid) instrument('valid_user?.ldap_fluff', uid: uid) do |payload| - @ldap.user_exists? uid + ldap.user_exists? payload[:uid] end end @@ -95,28 +80,44 @@ def valid_user?(uid) # @return [Boolean] true if group exists def valid_group?(gid) instrument('valid_group?.ldap_fluff', gid: gid) do |payload| - @ldap.group_exists? gid + ldap.group_exists? payload[:gid] end end # @param [String] uid - # @return [Array, Net::LDAP::Entry] - def find_user(uid) + # @return [Array, Net::LDAP::Entry] + def find_user(uid, only = nil) instrument('find_user.ldap_fluff', uid: uid) do |payload| - @ldap.member_service.find_user(uid) + ldap.member_service.find_user payload[:uid], only end end # @param [String] gid - # @return [Array, Net::LDAP::Entry] - def find_group(gid) + # @return [Array, Net::LDAP::Entry] + def find_group(gid, only = nil) instrument('find_group.ldap_fluff', gid: gid) do |payload| - @ldap.member_service.find_group(gid) + ldap.member_service.find_group payload[:gid], only end end private + # @param [Config] config + # @return [Generic] + # @raise [RuntimeError] + def create_provider(config) + case config.server_type + when :posix + Posix.new(config) + when :active_directory + ActiveDirectory.new(config) + when :free_ipa + FreeIPA.new(config) + else + raise 'unknown server_type' + end + end + # @param [String] event # @param [Hash] payload # @yieldreturn [Hash] @@ -124,8 +125,8 @@ def instrument(event, payload = {}) payload = payload ? payload.dup : {} if instrumentation_service - instrumentation_service.instrument(event, payload) do |payload| - payload[:result] = yield(payload) if block_given? + instrumentation_service.instrument(event, payload) do |data| + data[:result] = yield(data) if block_given? end elsif block_given? yield(payload) diff --git a/lib/ldap_fluff/active_directory.rb b/lib/ldap_fluff/active_directory.rb index a77888e..bedac9e 100644 --- a/lib/ldap_fluff/active_directory.rb +++ b/lib/ldap_fluff/active_directory.rb @@ -1,51 +1,64 @@ # frozen_string_literal: true class LdapFluff::ActiveDirectory < LdapFluff::Generic + # @param [String] uid + # @param [String] password + # @param [Hash] opts + # @return [Boolean] def bind?(uid = nil, password = nil, opts = {}) - unless uid.include?(',') || uid.include?('\\') || opts[:search] == false + if opts[:search] != false && uid && !(uid.include?(',') || uid.include?('\\')) service_bind - user = @member_service.find_user(uid) - uid = user.first.dn if user && user.first + user = member_service.find_user(uid, true) + + uid = user.dn if user end - @ldap.auth(uid, password) - @ldap.bind + + super(uid, password) end # active directory stores group membership on a users model # TODO: query by group individually not like this + # + # @param [String] uid + # @param [Array] gids + # @return [Boolean] def user_in_groups?(uid, gids = [], all = false) - service_bind - return true if !gids || gids.empty? - - begin - groups = @member_service.find_user_groups(uid) - intersection = gids & groups - all ? (intersection == gids) : !intersection.empty? - rescue MemberService::UIDNotFoundException - false - end + super + rescue MemberService::UIDNotFoundException + false end private + # @param [Net::LDAP::Entry] search + # @param [Symbol] method + # @return [Array] def users_from_search_results(search, method) - users = [] + members = search.send method - search.send(method).each do |member| + # @type [Array] + users = members.map do |member| begin - entry = @member_service.find_by_dn(member).first + entry = member_service.find_by_dn(member, true) rescue MemberService::UIDNotFoundException - next + entry = nil end - objectclasses = entry.objectclass.map(&:downcase) - if !(%w[organizationalperson person userproxy] & objectclasses).empty? - users << @member_service.get_login_from_entry(entry) - elsif !(%w[organizationalunit group] & objectclasses).empty? - users << users_for_gid(entry.cn.first) - end + entry ? get_users_for_entry(entry) : nil end - users.flatten.uniq + users.flatten.compact.uniq + end + + # @param [Net::LDAP::Entry] entry + # @return [Array, String] + def get_users_for_entry(entry) + objectclasses = entry[:objectclass].map(&:downcase) + + if !(%w[organizationalperson person userproxy] & objectclasses).empty? + member_service.get_login_from_entry(entry) + elsif !(%w[organizationalunit group] & objectclasses).empty? + users_for_gid(entry[:cn].first) + end end end diff --git a/lib/ldap_fluff/ad_member_service.rb b/lib/ldap_fluff/ad_member_service.rb index ba8dcfc..080a9bf 100644 --- a/lib/ldap_fluff/ad_member_service.rb +++ b/lib/ldap_fluff/ad_member_service.rb @@ -5,47 +5,59 @@ class LdapFluff::ActiveDirectory::MemberService < LdapFluff::GenericMemberServic # @param [Net::LDAP] ldap # @param [Config] config def initialize(ldap, config) - @attr_login = (config.attr_login || 'samaccountname') + config.instance_variable_set(:@attr_login, 'samaccountname') unless config.attr_login super end - # get a list [] of LDAP groups for a given user - # in active directory, this means a recursive lookup + # get a list of LDAP groups for a given user in active directory, this means a recursive lookup + # @param [String] uid + # @return [Array] def find_user_groups(uid) - data = find_user(uid) - _groups_from_ldap_data(data.first) + # @type [Net::LDAP::Entry] + data = find_user(uid, true) + groups_from_ldap_data(data) end - # return the :memberof attrs + parents, recursively - def _groups_from_ldap_data(payload) - data = [] - if !payload.nil? - first_level = payload[:memberof] - total_groups, = _walk_group_ancestry(first_level, first_level) - data = (get_groups(first_level + total_groups)).uniq - end - data + private + + # @param [Net::LDAP::Entry] payload + # @return [Array] the :memberof attrs + parents, recursively + def groups_from_ldap_data(payload) + return [] unless payload + + first_level = payload[:memberof] + total_groups, = walk_group_ancestry(first_level, first_level) + + get_groups(first_level + total_groups).uniq end # recursively loop over the parent list - def _walk_group_ancestry(group_dns = [], known_groups = []) - set = [] + # @param [Array] group_dns + # @param [Array] known_groups + # @return [Array>] + def walk_group_ancestry(group_dns = [], known_groups = [], set = []) group_dns.each do |group_dn| - search = @ldap.search(base: group_dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: ['memberof']) - if !search.nil? && !search.first.nil? - groups = search.first[:memberof] - known_groups - known_groups += groups - next_level, = _walk_group_ancestry(groups, known_groups) - set += next_level - set += groups - known_groups += next_level - end + groups = find_parent_groups(group_dn) + next unless groups + + groups -= known_groups + known_groups += groups + next_level, = walk_group_ancestry(groups, known_groups) # new_known_groups + + set += next_level + groups + known_groups += next_level end + [set, known_groups] end - def class_filter - Net::LDAP::Filter.eq('objectclass', 'group') + # @param [String] group_dn + # @return [Array] + def find_parent_groups(group_dn) + search = ldap.search(base: group_dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: ['memberof']) + + search = search.first if search + search ? search[:memberof] : nil end class UIDNotFoundException < LdapFluff::Error diff --git a/lib/ldap_fluff/config.rb b/lib/ldap_fluff/config.rb index d70bfd9..401b369 100644 --- a/lib/ldap_fluff/config.rb +++ b/lib/ldap_fluff/config.rb @@ -77,19 +77,19 @@ def convert(config) end # @param [Hash] config - def missing_keys?(config) + def check_missing_keys(config) missing_keys = ATTRIBUTES - config.keys raise ConfigError, "missing configuration for keys: #{missing_keys.join(',')}" unless missing_keys.empty? end # @param [Hash] config - def unknown_keys?(config) + def check_unknown_keys(config) unknown_keys = config.keys - ATTRIBUTES raise ConfigError, "unknown configuration keys: #{unknown_keys.join(',')}" unless unknown_keys.empty? end # @param [Hash] config - def all_required_keys?(config) + def check_required_keys(config) [:host, :port, :base_dn, :group_base, :server_type].each do |key| raise ConfigError, "config key #{key} has to be set, it was nil" unless config[key] end @@ -100,14 +100,14 @@ def all_required_keys?(config) end # @param [Hash] config - def anon_queries_set?(config) + def check_anon_queries_set(config) return if [false, true].include?(config[:anon_queries]) raise ConfigError, "config key anon_queries has to be true or false but was #{config[:anon_queries]}" end # @param [Hash] config - def correct_server_type?(config) + def check_server_type(config) return if [:posix, :active_directory, :free_ipa].include?(config[:server_type]) raise ConfigError, @@ -119,12 +119,13 @@ def correct_server_type?(config) # @raise [ConfigError] if config contains invalid keys def validate(config) config = DEFAULT_CONFIG.merge(config) + config[:group_base] = config[:base_dn] if !config[:group_base] || config[:group_base].empty? - correct_server_type?(config) - missing_keys?(config) - unknown_keys?(config) - all_required_keys?(config) - anon_queries_set?(config) + check_server_type(config) + check_missing_keys(config) + check_unknown_keys(config) + check_required_keys(config) + check_anon_queries_set(config) config end diff --git a/lib/ldap_fluff/freeipa.rb b/lib/ldap_fluff/freeipa.rb index d330437..fa22fd3 100644 --- a/lib/ldap_fluff/freeipa.rb +++ b/lib/ldap_fluff/freeipa.rb @@ -1,47 +1,62 @@ # frozen_string_literal: true class LdapFluff::FreeIPA < LdapFluff::Generic + # @param [String] uid + # @param [String] password + # @param [Hash] opts + # @return [Boolean] def bind?(uid = nil, password = nil, opts = {}) - unless uid.include?(',') - unless opts[:search] == false - service_bind - user = @member_service.find_user(uid) - end - uid = user && user.first ? user.first.dn : "uid=#{uid},cn=users,cn=accounts,#{@base}" + unless !uid || uid.include?(',') + user = + if opts[:search] != false + service_bind + member_service.find_user(uid, true) + end + + uid = user ? user.dn : "uid=#{uid},cn=users,cn=accounts,#{config.base_dn}" end - @ldap.auth(uid, password) - @ldap.bind + + super(uid, password) end + # @param [String] uid + # @return [Array] + # @raise [UnauthenticatedException] def groups_for_uid(uid) - begin - super - rescue MemberService::InsufficientQueryPrivilegesException - raise UnauthenticatedException, 'Insufficient Privileges to query groups data' - end + super + rescue MemberService::InsufficientQueryPrivilegesException + raise UnauthenticatedException, 'Insufficient Privileges to query groups data' end private + # Member results come in the form uid=sampleuser,cn=users, etc.. or gid=samplegroup,cn=groups + # @param [Net::LDAP::Entry] search + # @param [Symbol] method + # @return [Array] def users_from_search_results(search, method) - # Member results come in the form uid=sampleuser,cn=users, etc.. or gid=samplegroup,cn=groups - users = [] - - members = search.send(method) - - if method == :nisnetgrouptriple - users = @member_service.get_netgroup_users(members) - else - members.each do |member| - type = member.downcase.split(',')[1] - if type == 'cn=users' - users << @member_service.get_logins([member]) - elsif type == 'cn=groups' - users << users_for_gid(member.split(',')[0].split('=')[1]) - end + members = search.send method + + # @type [Array] + users = + if method == :nisnetgrouptriple + member_service.get_netgroup_users(members) + else + members.map { |member| get_users_for_member(member) } end - end - users.flatten.uniq + users.flatten.compact.uniq + end + + # @param [String] member DN + # @return [Array, String] + def get_users_for_member(member) + type = member.downcase.split(',')[1] + + if type == 'cn=users' + member_service.get_logins([member]) + elsif type == 'cn=groups' + users_for_gid(member.split(',').first.split('=')[1]) + end end end diff --git a/lib/ldap_fluff/freeipa_member_service.rb b/lib/ldap_fluff/freeipa_member_service.rb index e870d4b..a3f69bf 100644 --- a/lib/ldap_fluff/freeipa_member_service.rb +++ b/lib/ldap_fluff/freeipa_member_service.rb @@ -4,7 +4,7 @@ class LdapFluff::FreeIPA::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap # @param [Config] config def initialize(ldap, config) - @attr_login = (config.attr_login || 'uid') + config.instance_variable_set(:@attr_login, 'uid') unless config.attr_login super end @@ -15,21 +15,22 @@ def find_user_groups(uid) user = find_user(uid) # if group data is missing, they aren't querying with a user with enough privileges - user.delete_if { |u| u.nil? || !u.respond_to?(:attribute_names) || !u.attribute_names.include?(:memberof) } - raise InsufficientQueryPrivilegesException if user.size < 1 + user.delete_if { |u| !u.respond_to?(:attribute_names) || !u.attribute_names.include?(:memberof) } + raise InsufficientQueryPrivilegesException if user.empty? - get_groups(user[0][:memberof]) + get_groups(user.first[:memberof]) end # extract the group names from the LDAP style response, # @param [Array] grouplist # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) - grouplist.map(&:downcase).collect do |g| - if g.match(/.*?ipauniqueid=(.*?)/) - @ldap.search(base: g)[0][:cn][0] + grouplist.map do |g| + if g =~ /.*?ipauniqueid=(.*?)/i + search = (ldap.search(base: g.downcase) || []).first + search ? search[:cn].first : nil else - g.sub(/.*?cn=(.*?),.*/, '\1') + g.downcase.sub(/.*?cn=(.*?),.*/, '\1') end end.compact end diff --git a/lib/ldap_fluff/freeipa_netgroup_member_service.rb b/lib/ldap_fluff/freeipa_netgroup_member_service.rb index 0221291..23b5258 100644 --- a/lib/ldap_fluff/freeipa_netgroup_member_service.rb +++ b/lib/ldap_fluff/freeipa_netgroup_member_service.rb @@ -4,12 +4,13 @@ class LdapFluff::FreeIPA::NetgroupMemberService < LdapFluff::FreeIPA::MemberServ # @param [String] uid # @return [Array] def find_user_groups(uid) - groups = [] - @ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: @group_base).each do |entry| + groups = ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: config.group_base) + return [] unless groups + + groups.map do |entry| members = get_netgroup_users(entry[:nisnetgrouptriple]) - groups << entry[:cn][0] if members.include? uid - end - groups + members.include?(uid) ? entry[:cn].first : nil + end.compact end end diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index 6af2f81..8d5a129 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# @abstract class LdapFluff::Generic # @!attribute [rw] ldap # @return [Net::LDAP] @@ -7,23 +8,14 @@ class LdapFluff::Generic # @return [GenericMemberService] attr_accessor :ldap, :member_service + # @return [Config] + attr_reader :config + # @param [Config] config def initialize(config) - @ldap = Net::LDAP.new( - host: config.host, - base: config.base_dn, - port: config.port, - encryption: config.encryption, - instrumentation_service: config.instrumentation_service - ) + @config = config - @bind_user = config.service_user - @bind_pass = config.service_pass - @anon = config.anon_queries - @attr_login = config.attr_login - @base = config.base_dn - @group_base = (config.group_base.empty? ? config.base_dn : config.group_base) - @use_netgroups = config.use_netgroups + @ldap = create_ldap_client(config) @member_service = create_member_service(config) end @@ -31,99 +23,132 @@ def initialize(config) # @return [Boolean] def user_exists?(uid) service_bind - @member_service.find_user(uid) + member_service.find_user(uid) true rescue self.class::MemberService::UIDNotFoundException false end + # @param [String] gid + # @return [Boolean] def group_exists?(gid) service_bind - @member_service.find_group(gid) + member_service.find_group(gid) true rescue self.class::MemberService::GIDNotFoundException false end + # @param [String] uid + # @return [Array] def groups_for_uid(uid) service_bind - @member_service.find_user_groups(uid) + member_service.find_user_groups(uid) rescue self.class::MemberService::UIDNotFoundException - return [] + [] end + # @param [String] gid + # @return [Array] def users_for_gid(gid) return [] unless group_exists?(gid) - search = @member_service.find_group(gid).last + + search = member_service.find_group(gid, false) method = select_member_method(search) - return [] if method.nil? - users_from_search_results(search, method) + + method ? users_from_search_results(search, method) : [] end # returns whether a user is a member of ALL or ANY particular groups - # note: this method is much faster than groups_for_uid - # - # gids should be an array of group common names + # @note this method is much faster than groups_for_uid # - # returns true if owner is in ALL of the groups if all=true, otherwise - # returns true if owner is in ANY of the groups + # @param [String] uid + # @param [Array] gids should be an array of group common names + # @return [Boolean] + # returns true if owner is in ALL of the groups if all=true, otherwise + # returns true if owner is in ANY of the groups def user_in_groups?(uid, gids = [], all = true) service_bind - groups = @member_service.find_user_groups(uid).sort - gids = gids.sort - if all - groups & gids == gids - else - (groups & gids).any? - end + return true if !gids || gids.empty? + + groups = member_service.find_user_groups(uid) + intersection = gids & (groups || []) + + all ? (intersection.sort == gids.sort) : !intersection.empty? end + # @param [String] cn + # @return [Boolean] def includes_cn?(cn) filter = Net::LDAP::Filter.eq('cn', cn) - results = @ldap.search(base: @ldap.base, filter: filter) + result = ldap.search(base: ldap.base, filter: filter) # NOTE: present? - !(results.respond_to?(:empty?) ? results.empty? : !results) + !(result.respond_to?(:empty?) ? result.empty? : !result) end + # @raise [UnauthenticatedException] def service_bind - unless @anon || bind?(@bind_user, @bind_pass, search: false) - raise UnauthenticatedException, - "Could not bind to #{class_name} user #{@bind_user}" - end + return if config.anon_queries || bind?(config.service_user, config.service_pass, search: false) + + raise UnauthenticatedException, "Could not bind to #{class_name} user #{config.service_user}" + end + + # @param [String] uid + # @param [String] password + # @return [Boolean] + # @abstract + def bind?(uid = nil, password = nil, _ = {}) + ldap.auth(uid, password) + ldap.bind end private + # @param [Net::LDAP::Entry] search_result + # @return [Symbol] def select_member_method(search_result) - if @use_netgroups + return nil unless search_result + + if config.use_netgroups :nisnetgrouptriple else [:member, :memberuid, :uniquemember].find { |m| search_result.respond_to? m } end end + # @param [Config] config + # @return [Net::LDAP] + def create_ldap_client(config) + Net::LDAP.new( + host: config.host, + port: config.port, + base: config.base_dn, + encryption: config.encryption, + instrumentation_service: config.instrumentation_service + ) + end + + # @param [Config] config + # @return [GenericMemberService] def create_member_service(config) - if @use_netgroups + if config.use_netgroups self.class::NetgroupMemberService.new(@ldap, config) else self.class::MemberService.new(@ldap, config) end end + # @return [String] def class_name self.class.name.split('::').last end + # @param [Net::LDAP::Entry] search + # @param [Symbol] method + # @return [Array] + # @abstract def users_from_search_results(search, method) - members = search.send method - if method == :memberuid - # memberuid contains an array ['user1','user2'], no need to parse it - members - elsif method == :nisnetgrouptriple - @member_service.get_netgroup_users(members) - else - @member_service.get_logins(members) - end + raise NotImplementedError, "#{search.inspect}, #{method.inspect}" end class UnauthenticatedException < LdapFluff::Error diff --git a/lib/ldap_fluff/generic_member_service.rb b/lib/ldap_fluff/generic_member_service.rb index 9cfecf1..d342d7d 100644 --- a/lib/ldap_fluff/generic_member_service.rb +++ b/lib/ldap_fluff/generic_member_service.rb @@ -1,100 +1,136 @@ # frozen_string_literal: true +# @abstract class LdapFluff::GenericMemberService # @return [Net::LDAP] attr_accessor :ldap + # @!attribute [r] config + # @return [Config] + # @!attribute [r] search_filter + # @return [Net::LDAP::Filter] + attr_reader :config, :search_filter + # @param [Net::LDAP] ldap # @param [Config] config def initialize(ldap, config) - @ldap = ldap - @base = config.base_dn - @group_base = (config.group_base.empty? ? config.base_dn : config.group_base) - - @search_filter = nil - begin - @search_filter = Net::LDAP::Filter.construct(config.search_filter) unless - !config.search_filter || config.search_filter.empty? - rescue Net::LDAP::LdapError => e - puts "Search filter unavailable - #{e}" - end + @ldap = ldap + @config = config + + @search_filter = try_create_filter(config.search_filter) + end + + # @param [String] filter + # @return [Net::LDAP::Filter] + def try_create_filter(filter, kind = :Search) + !filter || filter.empty? ? nil : Net::LDAP::Filter.construct(filter) + rescue Net::LDAP::Error => e + warn "#{kind} filter unavailable - #{e}" + nil end # @param [String] uid - # @return [Array, Net::LDAP::Entry] + # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] - def find_user(uid) - user = @ldap.search(filter: name_filter(uid)) - raise self.class::UIDNotFoundException if (user.nil? || user.empty?) + def find_user(uid, only = nil) + # @type [Array] + user = ldap.search(filter: name_filter(uid)) + raise self.class::UIDNotFoundException if !user || user.empty? - user + return_one_or_all(user, only) end # @param [String] dn - # @return [Array, Net::LDAP::Entry] + # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] - def find_by_dn(dn) + def find_by_dn(dn, only = nil) # @type [String] entry entry, base = dn.split(/(?] + user = ldap.search(filter: name_filter(entry_value, entry_attr), base: base) raise self.class::UIDNotFoundException if !user || user.empty? - user + return_one_or_all(user, only) end - def find_group(gid) - group = @ldap.search(filter: group_filter(gid), base: @group_base) + # @param [String] gid + # @return [Array, Net::LDAP::Entry] + # @raise [UIDNotFoundException] + def find_group(gid, only = nil) + # @type [Array] + group = ldap.search(filter: group_filter(gid), base: config.group_base) raise self.class::GIDNotFoundException if !group || group.empty? - group + return_one_or_all(group, only) end - def name_filter(uid, attr = @attr_login) - filter = Net::LDAP::Filter.eq(attr, uid) + # @param [String] uid + # @return [Array] + # @abstract + def find_user_groups(uid) + raise NotImplementedError, uid.inspect + end - if @search_filter.nil? - filter - else - filter & @search_filter - end + # @param [String] uid + # @return [Net::LDAP::Filter] + def name_filter(uid, attr = nil) + filter = Net::LDAP::Filter.eq(attr || config.attr_login, uid) + search_filter ? (filter & search_filter) : filter end + # @param [String] gid + # @return [Net::LDAP::Filter] def group_filter(gid) Net::LDAP::Filter.eq('cn', gid) end # extract the group names from the LDAP style response, - # return string will be something like - # CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com + # @param [Array] grouplist + # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) grouplist.map { |g| g.downcase.sub(/.*?cn=(.*?),.*/, '\1') } end + # @param [Array] netgroup_triples + # @return [Array] def get_netgroup_users(netgroup_triples) return [] unless netgroup_triples netgroup_triples.map { |m| m.split(',')[1] } end + # @param [Array] userlist + # @return [Array] def get_logins(userlist) - userlist.map(&:downcase!) - [@attr_login, 'uid', 'cn'].map do |attribute| + userlist.map!(&:downcase) + + results = [config.attr_login, 'uid', 'cn'].map do |attribute| logins = userlist.map { |g| g.sub(/.*?#{attribute}=(.*?),.*/, '\1') } - if logins == userlist - nil - else - logins - end - end.uniq.compact.flatten + logins == userlist ? nil : logins + end + + results.flatten.compact.uniq end + # @param [Net::LDAP::Entry] entry + # @return [String] def get_login_from_entry(entry) - [@attr_login, 'uid', 'cn'].each do |attribute| + [config.attr_login, 'uid', 'cn'].each do |attribute| return entry.send(attribute) if entry.respond_to? attribute end + nil end + + protected + + # @param [Array] arr + def return_one_or_all(arr, only = nil) + return arr if only.nil? + + only ? arr.first : arr.last + end end diff --git a/lib/ldap_fluff/posix.rb b/lib/ldap_fluff/posix.rb index ce0cadd..ff74893 100644 --- a/lib/ldap_fluff/posix.rb +++ b/lib/ldap_fluff/posix.rb @@ -1,40 +1,54 @@ # frozen_string_literal: true class LdapFluff::Posix < LdapFluff::Generic + # @param [String] uid + # @param [String] password + # @param [Hash] opts + # @return [Boolean] def bind?(uid = nil, password = nil, opts = {}) - unless uid.include?(',') || opts[:search] == false + if opts[:search] != false && uid && !uid.include?(',') service_bind - user = @member_service.find_user(uid) - uid = user.first.dn if user && user.first + user = member_service.find_user(uid, true) + + uid = user.dn if user end - @ldap.auth(uid, password) - @ldap.bind + + super(uid, password) end private + # @return [Net::LDAP::Filter] + def group_class_filter + if config.use_netgroups + Net::LDAP::Filter.eq('objectClass', 'nisNetgroup') + else + Net::LDAP::Filter.eq('objectClass', 'posixGroup') | + Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | + Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | + Net::LDAP::Filter.eq('objectClass', 'groupOfNames') + end + end + # To find groups in standard LDAP without group membership attributes # we have to look for OUs or posixGroups within the current group scope, # i.e: cn=ldapusers,ou=groups,dc=example,dc=com -> cn=myusers,cn=ldapusers,ou=gr... + # + # @param [Net::LDAP::Entry] search + # @param [Symbol] method + # @return [Array] def users_from_search_results(search, method) - if @use_netgroups - filter = Net::LDAP::Filter.eq('objectClass', 'nisNetgroup') - else - filter = - Net::LDAP::Filter.eq('objectClass', 'posixGroup') | - Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | - Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | - Net::LDAP::Filter.eq('objectClass', 'groupOfNames') - end - groups = @ldap.search(base: search.dn, filter: filter) + groups = ldap.search(base: search.dn, filter: group_class_filter) members = groups.map { |group| group.send(method) }.flatten.uniq - if method == :memberuid + case method + when :memberuid + # memberuid contains an array ['user1','user2'], no need to parse it members - elsif method == :nisnetgrouptriple - @member_service.get_netgroup_users(members) + when :nisnetgrouptriple + member_service.get_netgroup_users(members) else - @member_service.get_logins(members) + member_service.get_logins(members) end end end diff --git a/lib/ldap_fluff/posix_member_service.rb b/lib/ldap_fluff/posix_member_service.rb index 2b33e26..eec93d2 100644 --- a/lib/ldap_fluff/posix_member_service.rb +++ b/lib/ldap_fluff/posix_member_service.rb @@ -5,53 +5,48 @@ class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap # @param [Config] config def initialize(ldap, config) - @attr_login = (config.attr_login || 'memberuid') + config.instance_variable_set(:@attr_login, 'memberuid') unless config.attr_login super end # @param [String] uid - # @return [Array, Net::LDAP::Entry] + # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] - def find_user(uid, base_dn = @base) - user = @ldap.search(filter: name_filter(uid), base: base_dn) - raise UIDNotFoundException if (user.nil? || user.empty?) + def find_user(uid, only = nil, base_dn = nil) + if only.is_a?(String) + base_dn ||= only + only = nil + else + base_dn ||= config.base_dn + end + + # @type [Array] + user = ldap.search(filter: name_filter(uid), base: base_dn) + raise UIDNotFoundException if !user || user.empty? - user + return_one_or_all(user, only) end # @param [String] uid # @return [Array] an LDAP user with groups attached # @note this method is not particularly fast for large LDAP systems def find_user_groups(uid) - groups = [] - @ldap.search(filter: Net::LDAP::Filter.eq('memberuid', uid), base: @group_base).each do |entry| - groups << entry[:cn][0] - end - groups + groups = ldap.search(filter: Net::LDAP::Filter.eq('memberuid', uid), base: config.group_base) + return [] unless groups + + groups.map { |entry| entry[:cn].first } end # @param [String] uid # @param [Array] gids # @deprecated - def times_in_groups(uid, gids, all) - filters = [] - gids.each do |cn| - filters << group_filter(cn) - end - group_filters = merge_filters(filters, all) + def times_in_groups(uid, gids, all = false) + filters = gids.map { |cn| group_filter(cn) } + # AND or OR all of the filters together + group_filters = filters.reduce(all ? :& : :|) filter = name_filter(uid) & group_filters - @ldap.search(base: @group_base, filter: filter).size - end - # AND or OR all of the filters together - def merge_filters(filters = [], all = false) - if !filters.nil? && filters.size >= 1 - filter = filters[0] - filters[1..(filters.size - 1)].each do |gfilter| - filter = (all ? filter & gfilter : filter | gfilter) - end - return filter - end + (ldap.search(base: config.group_base, filter: filter) || []).size end class UIDNotFoundException < LdapFluff::Error diff --git a/lib/ldap_fluff/posix_netgroup_member_service.rb b/lib/ldap_fluff/posix_netgroup_member_service.rb index f0c07fb..98b8aee 100644 --- a/lib/ldap_fluff/posix_netgroup_member_service.rb +++ b/lib/ldap_fluff/posix_netgroup_member_service.rb @@ -5,12 +5,13 @@ class LdapFluff::Posix::NetgroupMemberService < LdapFluff::Posix::MemberService # @param [String] uid # @return [Array] list of group CNs for a user def find_user_groups(uid) - groups = [] - @ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: @group_base).each do |entry| + groups = ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: config.group_base) + return [] unless groups + + groups.map do |entry| members = get_netgroup_users(entry[:nisnetgrouptriple]) - groups << entry[:cn][0] if members.include? uid - end - groups + members.include?(uid) ? entry[:cn].first : nil + end.compact end end diff --git a/test/ad_member_services_test.rb b/test/ad_member_services_test.rb index bcbfc8c..056ffd7 100644 --- a/test/ad_member_services_test.rb +++ b/test/ad_member_services_test.rb @@ -7,105 +7,102 @@ class TestADMemberService < MiniTest::Test def setup super - @adms = LdapFluff::ActiveDirectory::MemberService.new(@ldap, @config) - @gfilter = group_filter('group') & group_class_filter + # noinspection RubyYardParamTypeMatch + @adms = LdapFluff::ActiveDirectory::MemberService.new(ldap, config) end def basic_user - @ldap.expect(:search, ad_user_payload, [filter: ad_name_filter('john')]) - @ldap.expect(:search, ad_parent_payload(1), [base: ad_group_dn, scope: 0, attributes: ['memberof']]) + ldap.expect(:search, ad_user_payload, [filter: ad_name_filter('john')]) + ldap.expect(:search, ad_parent_payload(1), [base: ad_group_dn, scope: 0, attributes: ['memberof']]) + @adms.ldap = ldap end def basic_group - @ldap.expect(:search, ad_group_payload, [filter: ad_group_filter('broze'), base: @config.group_base]) + ldap.expect(:search, ad_group_payload, [filter: ad_group_filter('broze'), base: config.group_base]) + @adms.ldap = ldap end - def nest_deep(n) + def nest_deep(num, ret_method = :ad_parent_payload) # add all the expects - 1.upto(n - 1) do |i| - @ldap.expect(:search, ad_parent_payload(i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) + 1.upto(num - 1) do |i| + ldap.expect(:search, send(ret_method, i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) end + # terminate or we loop FOREVER - @ldap.expect(:search, [], [base: ad_group_dn("bros#{n}"), scope: 0, attributes: ['memberof']]) + ldap.expect(:search, [], [base: ad_group_dn("bros#{num}"), scope: 0, attributes: ['memberof']]) end - def double_nested(n) - # add all the expects - 1.upto(n - 1) do |i| - @ldap.expect(:search, ad_double_payload(i + 1), [base: ad_group_dn("bros#{i}"), scope: 0, attributes: ['memberof']]) - end - # terminate or we loop FOREVER - @ldap.expect(:search, [], [base: ad_group_dn("bros#{n}"), scope: 0, attributes: ['memberof']]) - (n - 1).downto(1) do |j| - @ldap.expect(:search, [], [base: ad_group_dn("broskies#{j + 1}"), scope: 0, attributes: ['memberof']]) + def double_nested(num) + nest_deep(num, :ad_double_payload) + + (num - 1).downto(1) do |j| + ldap.expect(:search, [], [base: ad_group_dn("broskies#{j + 1}"), scope: 0, attributes: ['memberof']]) end end def test_find_user basic_user - @ldap.expect(:search, [], [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) - @adms.ldap = @ldap + ldap.expect(:search, [], [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) + assert_equal(%w[group bros1], @adms.find_user_groups('john')) - @ldap.verify end def test_nested_groups basic_user # basic user is memberof 'group'... and 'group' is memberof 'bros1' # now make 'bros1' be memberof 'group' again - @ldap.expect(:search, ad_user_payload, [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) - @adms.ldap = @ldap + ldap.expect(:search, ad_user_payload, [base: ad_group_dn('bros1'), scope: 0, attributes: ['memberof']]) + assert_equal(%w[group bros1], @adms.find_user_groups('john')) - @ldap.verify end def test_missing_user - @ldap.expect(:search, nil, [filter: ad_name_filter('john')]) - @adms.ldap = @ldap + ldap.expect(:search, nil, [filter: ad_name_filter('john')]) + @adms.ldap = ldap + assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_user_groups('john').data end - @ldap.verify end def test_some_deep_recursion basic_user nest_deep(25) - @adms.ldap = @ldap + assert_equal(26, @adms.find_user_groups('john').size) - @ldap.verify end def test_complex_recursion basic_user double_nested(5) - @adms.ldap = @ldap + assert_equal(10, @adms.find_user_groups('john').size) - @ldap.verify end def test_nil_payload - assert_equal([], @adms._groups_from_ldap_data(nil)) + assert_equal([], @adms.send(:groups_from_ldap_data, nil)) end def test_empty_user - @ldap.expect(:search, [], [filter: ad_name_filter('john')]) - @adms.ldap = @ldap + ldap.expect(:search, [], [filter: ad_name_filter('john')]) + @adms.ldap = ldap + assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_user_groups('john').data end - @ldap.verify end def test_find_good_user - basic_user - @adms.ldap = @ldap - assert_equal(ad_user_payload, @adms.find_user('john')) + ldap.expect(:search, user = ad_user_payload, [filter: ad_name_filter('john')]) + @adms.ldap = ldap + + assert_equal(user.dup, @adms.find_user('john')) end def test_find_missing_user - @ldap.expect(:search, nil, [filter: ad_name_filter('john')]) - @adms.ldap = @ldap + ldap.expect(:search, nil, [filter: ad_name_filter('john')]) + @adms.ldap = ldap + assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_user('john') end @@ -113,48 +110,49 @@ def test_find_missing_user def test_find_good_group basic_group - @adms.ldap = @ldap assert_equal(ad_group_payload, @adms.find_group('broze')) end def test_find_missing_group - @ldap.expect(:search, nil, [filter: ad_group_filter('broze'), base: @config.group_base]) - @adms.ldap = @ldap + ldap.expect(:search, nil, [filter: ad_group_filter('broze'), base: config.group_base]) + @adms.ldap = ldap + assert_raises(LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException) do @adms.find_group('broze') end end def test_find_by_dn - @ldap.expect(:search, [:result], [filter: Net::LDAP::Filter.eq('cn', 'Foo Bar'), base: 'dc=example,dc=com']) - @adms.ldap = @ldap + ldap.expect(:search, [:result], [filter: ad_group_filter('Foo Bar'), base: 'dc=example,dc=com']) + @adms.ldap = ldap + assert_equal([:result], @adms.find_by_dn('cn=Foo Bar,dc=example,dc=com')) - @ldap.verify end + # In at least one AD installation, users who have commas in their CNs are + # returned by the server in answer to a group membership query with + # backslashes before the commas in the CNs. Such escaped commas should not + # be used when splitting the DN. def test_find_by_dn_comma_in_cn - # In at least one AD installation, users who have commas in their CNs are - # returned by the server in answer to a group membership query with - # backslashes before the commas in the CNs. Such escaped commas should not - # be used when splitting the DN. - @ldap.expect(:search, [:result], [filter: Net::LDAP::Filter.eq('cn', 'Bar, Foo'), base: 'dc=example,dc=com']) - @adms.ldap = @ldap + ldap.expect(:search, [:result], [filter: ad_group_filter('Bar, Foo'), base: 'dc=example,dc=com']) + @adms.ldap = ldap + assert_equal([:result], @adms.find_by_dn('cn=Bar\, Foo,dc=example,dc=com')) - @ldap.verify end def test_find_by_dn_missing_entry - @ldap.expect(:search, nil, [filter: Net::LDAP::Filter.eq('cn', 'Foo Bar'), base: 'dc=example,dc=com']) - @adms.ldap = @ldap + ldap.expect(:search, nil, [filter: ad_group_filter('Foo Bar'), base: 'dc=example,dc=com']) + @adms.ldap = ldap + assert_raises(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) do @adms.find_by_dn('cn=Foo Bar,dc=example,dc=com') end - @ldap.verify end def test_get_login_from_entry entry = Net::LDAP::Entry.new('Example User') - entry['sAMAccountName'] = 'example' + entry[:sAMAccountName] = 'example' + assert_equal(['example'], @adms.get_login_from_entry(entry)) end diff --git a/test/ad_test.rb b/test/ad_test.rb index 105aad4..ab354b8 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -7,75 +7,62 @@ class TestAD < MiniTest::Test def setup super - @ad = LdapFluff::ActiveDirectory.new(@config) - end - - # default setup for service bind users - def service_bind - @ldap.expect(:auth, nil, %w[service pass]) - super + @ad = LdapFluff::ActiveDirectory.new(config) end def test_good_bind # no expectation on the service account - @ldap.expect(:auth, nil, %w[EXAMPLE\\internet password]) - @ldap.expect(:bind, true) - @ad.ldap = @ldap - assert_equal(@ad.bind?('EXAMPLE\\internet', 'password'), true) - @ldap.verify + service_bind('EXAMPLE\\internet', 'password') + + assert @ad.bind?('EXAMPLE\\internet', 'password') end def test_good_bind_with_dn # no expectation on the service account - @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), 'password']) - @ldap.expect(:bind, true) - @ad.ldap = @ldap - assert_equal(@ad.bind?(ad_user_dn('Internet User'), 'password'), true) - @ldap.verify + service_bind(user = ad_user_dn('Internet User'), 'password') + + assert @ad.bind?(user.dup, 'password') end + # looks up the account name's full DN via the service account def test_good_bind_with_account_name - # looks up the account name's full DN via the service account - @md = MiniTest::Mock.new - user_result = MiniTest::Mock.new - user_result.expect(:dn, ad_user_dn('Internet User')) - @md.expect(:find_user, [user_result], %w[internet]) - @ad.member_service = @md + user_result.expect(:dn, user = ad_user_dn('Internet User')) + md.expect(:find_user, user_result, ['internet', true]) + @ad.member_service = md + service_bind - @ldap.expect(:auth, nil, [ad_user_dn('Internet User'), 'password']) - @ldap.expect(:bind, true) - assert_equal(@ad.bind?('internet', 'password'), true) - @ldap.verify + service_bind(user.dup, 'password') + + assert @ad.bind?('internet', 'password') end def test_bad_bind - @ldap.expect(:auth, nil, %w[EXAMPLE\\internet password]) - @ldap.expect(:bind, false) - @ad.ldap = @ldap - assert_equal(@ad.bind?('EXAMPLE\\internet', 'password'), false) - @ldap.verify + service_bind('EXAMPLE\\internet', 'password', false) + + refute @ad.bind?('EXAMPLE\\internet', 'password') end def test_groups service_bind basic_user - assert_equal(@ad.groups_for_uid('john'), %w[bros]) + + assert_equal %w[bros], @ad.groups_for_uid('john') end def test_bad_user service_bind - md = MiniTest::Mock.new + md.expect(:find_user_groups, nil) do |uid| - raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' + uid != 'john' || raise(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) end @ad.member_service = md - assert_equal(@ad.groups_for_uid('john'), []) + + assert_equal [], @ad.groups_for_uid('john') end def test_bad_service_user - @ldap.expect(:auth, nil, %w[service pass]) - @ldap.expect(:bind, false) - @ad.ldap = @ldap + service_bind('service', 'pass', false) + assert_raises(LdapFluff::ActiveDirectory::UnauthenticatedException) do @ad.groups_for_uid('john') end @@ -84,127 +71,149 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ad.user_in_groups?('john', %w[bros], false), true) + + assert @ad.user_in_groups?('john', %w[bros], false) end def test_is_some_groups service_bind basic_user - assert_equal(@ad.user_in_groups?('john', %w[bros buds], false), true) + + assert @ad.user_in_groups?('john', %w[bros buds], false) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ad.user_in_groups?('john', %w[bros buds], true), false) + + refute @ad.user_in_groups?('john', %w[bros buds], true) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ad.user_in_groups?('john', %w[broskies], false), false) + + refute @ad.user_in_groups?('john', %w[broskies], false) end def test_group_subset service_bind bigtime_user - assert_equal(@ad.user_in_groups?('john', %w[broskies], true), true) + + assert @ad.user_in_groups?('john', %w[broskies], true) + end + + def basic_group(ret = nil, name = 'foremaners') + md.expect(:find_group, [ret], [name]) + md.expect(:find_group, ret, [name, false]) end def test_subgroups_in_groups_are_ignored group = Net::LDAP::Entry.new('foremaners') - md = MiniTest::Mock.new - 2.times { md.expect(:find_group, [group], ['foremaners']) } - 2.times { service_bind } + basic_group(group) + service_bind # 2.times - # TODO: md.expect(:find_by_dn, nil) { raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException } + # NOTE: md.expect(:find_by_dn, nil) { raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException } @ad.member_service = md - assert_equal @ad.users_for_gid('foremaners'), [] - md.verify + assert_equal [], @ad.users_for_gid('foremaners') end def test_user_exists - md = MiniTest::Mock.new md.expect(:find_user, 'notnilluser', %w[john]) + @ad.member_service = md service_bind + assert(@ad.user_exists?('john')) end def test_missing_user - md = MiniTest::Mock.new md.expect(:find_user, nil) do |uid| - raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' + uid != 'john' || raise(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) end + @ad.member_service = md service_bind + refute(@ad.user_exists?('john')) end def test_group_exists - md = MiniTest::Mock.new md.expect(:find_group, 'notnillgroup', %w[broskies]) + @ad.member_service = md service_bind + assert(@ad.group_exists?('broskies')) end def test_missing_group - md = MiniTest::Mock.new md.expect(:find_group, nil) do |gid| - raise LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException if gid == 'broskies' + gid != 'broskies' || raise(LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException) end + @ad.member_service = md service_bind + refute(@ad.group_exists?('broskies')) end - def test_find_users_in_nested_groups + def nested_groups group = Net::LDAP::Entry.new('foremaners') - nested_group = Net::LDAP::Entry.new('katellers') - nested_user = Net::LDAP::Entry.new('testuser') - group[:member] = ['CN=katellers,DC=corp,DC=windows,DC=com'] + + nested_group = Net::LDAP::Entry.new('katellers') nested_group[:cn] = ['katellers'] - nested_group[:member] = ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com'] nested_group[:objectclass] = ['organizationalunit'] + nested_group[:member] = ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com'] + + nested_user = Net::LDAP::Entry.new('testuser') nested_user[:objectclass] = ['person'] - md = MiniTest::Mock.new - 2.times { md.expect(:find_group, [group], ['foremaners']) } - 2.times { md.expect(:find_group, [nested_group], ['katellers']) } + [group, nested_group, nested_user] + end + + def test_find_users_in_nested_groups + group, nested_group, nested_user = nested_groups + basic_group(group) + basic_group(nested_group, 'katellers') 2.times { service_bind } - md.expect(:find_by_dn, [nested_group], ['CN=katellers,DC=corp,DC=windows,DC=com']) - md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) + md.expect(:find_by_dn, nested_group, ['CN=katellers,DC=corp,DC=windows,DC=com', true]) + md.expect(:find_by_dn, nested_user, ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', true]) md.expect(:get_login_from_entry, 'testuser', [nested_user]) @ad.member_service = md - assert_equal @ad.users_for_gid('foremaners'), ['testuser'] - md.verify + + assert_equal ['testuser'], @ad.users_for_gid('foremaners') end - def test_find_users_with_empty_nested_group + def empty_nested_groups group = Net::LDAP::Entry.new('foremaners') - nested_group = Net::LDAP::Entry.new('katellers') - nested_user = Net::LDAP::Entry.new('testuser') - group[:member] = ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', 'CN=katellers,DC=corp,DC=windows,DC=com'] + + nested_group = Net::LDAP::Entry.new('katellers') nested_group[:cn] = ['katellers'] nested_group[:objectclass] = ['organizationalunit'] nested_group[:memberof] = ['CN=foremaners,DC=corp,DC=windows,DC=com'] + + nested_user = Net::LDAP::Entry.new('testuser') nested_user[:objectclass] = ['person'] - md = MiniTest::Mock.new - 2.times { md.expect(:find_group, [group], ['foremaners']) } - 2.times { md.expect(:find_group, [nested_group], ['katellers']) } + [group, nested_group, nested_user] + end + + def test_find_users_with_empty_nested_group + group, nested_group, nested_user = empty_nested_groups + basic_group(group) + basic_group(nested_group, 'katellers') 2.times { service_bind } - md.expect(:find_by_dn, [nested_user], ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com']) - md.expect(:find_by_dn, [nested_group], ['CN=katellers,DC=corp,DC=windows,DC=com']) + md.expect(:find_by_dn, nested_user, ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', true]) + md.expect(:find_by_dn, nested_group, ['CN=katellers,DC=corp,DC=windows,DC=com', true]) md.expect(:get_login_from_entry, 'testuser', [nested_user]) @ad.member_service = md - assert_equal @ad.users_for_gid('foremaners'), ['testuser'] - md.verify + + assert_equal ['testuser'], @ad.users_for_gid('foremaners') end end diff --git a/test/config_test.rb b/test/config_test.rb index d28762a..ee8d616 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -6,27 +6,29 @@ class ConfigTest < MiniTest::Test include LdapTestHelper def test_unsupported_type - assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new(config_hash.update server_type: 'inactive_directory') } + assert_raises(LdapFluff::Config::ConfigError) do + LdapFluff.new CONFIG_HASH.merge(server_type: 'inactive_directory') + end end def test_load_posix - ldap = LdapFluff.new(config_hash.update server_type: 'posix') - assert_instance_of LdapFluff::Posix, ldap.ldap + fluff = LdapFluff.new CONFIG_HASH.merge(server_type: 'posix') + assert_instance_of LdapFluff::Posix, fluff.ldap end def test_load_ad - ldap = LdapFluff.new(config_hash.update server_type: 'active_directory') - assert_instance_of LdapFluff::ActiveDirectory, ldap.ldap + fluff = LdapFluff.new CONFIG_HASH.merge(server_type: :active_directory) + assert_instance_of LdapFluff::ActiveDirectory, fluff.ldap end def test_load_free_ipa - ldap = LdapFluff.new(config_hash.update server_type: 'free_ipa') - assert_instance_of LdapFluff::FreeIPA, ldap.ldap + fluff = LdapFluff.new CONFIG_HASH.merge(server_type: 'free_ipa') + assert_instance_of LdapFluff::FreeIPA, fluff.ldap end def test_instrumentation_service is = Object.new - net_ldap = LdapFluff.new(config_hash.update instrumentation_service: is).ldap.ldap + net_ldap = LdapFluff.new(CONFIG_HASH.merge(instrumentation_service: is)).ldap.ldap assert_equal is, net_ldap.send(:instrumentation_service) end end diff --git a/test/ipa_member_services_test.rb b/test/ipa_member_services_test.rb index 3d72e4b..98f5247 100644 --- a/test/ipa_member_services_test.rb +++ b/test/ipa_member_services_test.rb @@ -7,51 +7,54 @@ class TestIPAMemberService < MiniTest::Test def setup super - @ipams = LdapFluff::FreeIPA::MemberService.new(@ldap, @config) + # noinspection RubyYardParamTypeMatch + @ipams = LdapFluff::FreeIPA::MemberService.new(ldap, config) end def basic_user - @ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) + ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) end def basic_group - @ldap.expect(:search, ipa_group_payload, [filter: ipa_group_filter('broze'), base: @config.group_base]) + ldap.expect(:search, ipa_group_payload, [filter: ipa_group_filter('broze'), base: config.group_base]) end def test_find_user basic_user - @ipams.ldap = @ldap + @ipams.ldap = ldap + assert_equal(%w[group bros], @ipams.find_user_groups('john')) - @ldap.verify end def test_missing_user - @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) - @ipams.ldap = @ldap + ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) + @ipams.ldap = ldap + assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do @ipams.find_user_groups('john').data end - @ldap.verify end def test_no_groups entry = Net::LDAP::Entry.new - entry['memberof'] = [] - @ldap.expect(:search, [Net::LDAP::Entry.new, entry], [filter: ipa_name_filter('john')]) - @ipams.ldap = @ldap + entry[:memberof] = [] + ldap.expect(:search, [Net::LDAP::Entry.new, entry], [filter: ipa_name_filter('john')]) + @ipams.ldap = ldap + assert_equal([], @ipams.find_user_groups('john')) - @ldap.verify end def test_find_good_user basic_user - @ipams.ldap = @ldap + @ipams.ldap = ldap + assert_equal(ipa_user_payload, @ipams.find_user('john')) end def test_find_missing_user - @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) - @ipams.ldap = @ldap + ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) + @ipams.ldap = ldap + assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do @ipams.find_user('john') end @@ -59,13 +62,15 @@ def test_find_missing_user def test_find_good_group basic_group - @ipams.ldap = @ldap + @ipams.ldap = ldap + assert_equal(ipa_group_payload, @ipams.find_group('broze')) end def test_find_missing_group - @ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: @config.group_base]) - @ipams.ldap = @ldap + ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: config.group_base]) + @ipams.ldap = ldap + assert_raises(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) do @ipams.find_group('broze') end diff --git a/test/ipa_netgroup_member_services_test.rb b/test/ipa_netgroup_member_services_test.rb index 2d419da..6318e36 100644 --- a/test/ipa_netgroup_member_services_test.rb +++ b/test/ipa_netgroup_member_services_test.rb @@ -6,62 +6,60 @@ class TestIPANetgroupMemberService < MiniTest::Test include LdapTestHelper def setup - netgroups_config super - @ipams = LdapFluff::FreeIPA::NetgroupMemberService.new(@ldap, netgroups_config) + # noinspection RubyYardParamTypeMatch + @ipams = LdapFluff::FreeIPA::NetgroupMemberService.new(ldap, netgroups_config) end def basic_user - @ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) + ldap.expect(:search, ipa_user_payload, [filter: ipa_name_filter('john')]) end def basic_group - @ldap.expect(:search, ipa_netgroup_payload('broze'), [filter: ipa_group_filter('broze'), base: @config.group_base]) + ldap.expect(:search, ipa_netgroup_payload('broze'), [filter: ipa_group_filter('broze'), base: config.group_base]) end def test_find_user basic_user - @ipams.ldap = @ldap + @ipams.ldap = ldap + assert_equal ipa_user_payload, @ipams.find_user('john') - @ldap.verify end def test_find_missing_user - @ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) - @ipams.ldap = @ldap - assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) do - @ipams.find_user('john') - end + ldap.expect(:search, nil, [filter: ipa_name_filter('john')]) + @ipams.ldap = ldap + + assert_raises(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) { @ipams.find_user('john') } end def test_find_user_groups - response = ipa_netgroup_payload('bros', ['(,john,)', '(,joe,)']) - @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - base: @config.group_base]) + response = ipa_netgroup_payload('bros', %w[(,john,) (,joe,)]) + ldap.expect(:search, response, [filter: group_class_filter('nisNetgroup'), base: config.group_base]) + @ipams.ldap = ldap - @ipams.ldap = @ldap assert_equal(['bros'], @ipams.find_user_groups('john')) - @ldap.verify end def test_find_no_user_groups response = ipa_netgroup_payload('bros', ['(,joe,)']) - @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - base: @config.group_base]) - @ipams.ldap = @ldap + ldap.expect(:search, response, [filter: group_class_filter('nisNetgroup'), base: config.group_base]) + @ipams.ldap = ldap + assert_equal([], @ipams.find_user_groups('john')) - @ldap.verify end def test_find_group basic_group - @ipams.ldap = @ldap + @ipams.ldap = ldap + assert_equal(ipa_netgroup_payload('broze'), @ipams.find_group('broze')) end def test_find_missing_group - @ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: @config.group_base]) - @ipams.ldap = @ldap + ldap.expect(:search, nil, [filter: ipa_group_filter('broze'), base: config.group_base]) + @ipams.ldap = ldap + assert_raises(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) do @ipams.find_group('broze') end diff --git a/test/ipa_test.rb b/test/ipa_test.rb index cefd566..4ac0f02 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -7,66 +7,60 @@ class TestIPA < MiniTest::Test def setup super - @ipa = LdapFluff::FreeIPA.new(@config) + @ipa = LdapFluff::FreeIPA.new(config) end # default setup for service bind users - def service_bind - @ldap.expect(:auth, nil, [ipa_user_bind('service'), 'pass']) - super + def service_bind(user = 'service', pass = 'pass', ret = true) + super(ipa_user_bind(user), pass, ret) end + # looks up the uid's full DN via the service account def test_good_bind - # looks up the uid's full DN via the service account - @md = MiniTest::Mock.new - user_result = MiniTest::Mock.new user_result.expect(:dn, ipa_user_bind('internet')) - @md.expect(:find_user, [user_result], %w[internet]) - @ipa.member_service = @md + md.expect(:find_user, user_result, ['internet', true]) + @ipa.member_service = md + service_bind - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) - @ldap.expect(:bind, true) - assert_equal(@ipa.bind?('internet', 'password'), true) - @ldap.verify + service_bind('internet', 'password') + + assert @ipa.bind?('internet', 'password') end def test_good_bind_with_dn # no expectation on the service account - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) - @ldap.expect(:bind, true) - @ipa.ldap = @ldap - assert_equal(@ipa.bind?(ipa_user_bind('internet'), 'password'), true) - @ldap.verify + service_bind('internet', 'password') + + assert @ipa.bind?(ipa_user_bind('internet'), 'password') end def test_bad_bind - @ldap.expect(:auth, nil, [ipa_user_bind('internet'), 'password']) - @ldap.expect(:bind, false) - @ipa.ldap = @ldap - assert_equal(@ipa.bind?(ipa_user_bind('internet'), 'password'), false) - @ldap.verify + service_bind('internet', 'password', false) + + refute @ipa.bind?(ipa_user_bind('internet'), 'password') end def test_groups service_bind basic_user - assert_equal(@ipa.groups_for_uid('john'), %w[bros]) + + assert_equal %w[bros], @ipa.groups_for_uid('john') end def test_bad_user service_bind - @md = MiniTest::Mock.new - @md.expect(:find_user_groups, nil) do |uid| - raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' + + md.expect(:find_user_groups, nil) do |uid| + uid != 'john' || raise(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) end - @ipa.member_service = @md - assert_equal(@ipa.groups_for_uid('john'), []) + @ipa.member_service = md + + assert_equal [], @ipa.groups_for_uid('john') end def test_bad_service_user - @ldap.expect(:auth, nil, [ipa_user_bind('service'), 'pass']) - @ldap.expect(:bind, false) - @ipa.ldap = @ldap + service_bind('service', 'pass', false) + assert_raises(LdapFluff::FreeIPA::UnauthenticatedException) do @ipa.groups_for_uid('john') end @@ -75,92 +69,106 @@ def test_bad_service_user def test_is_in_groups service_bind basic_user - assert_equal(@ipa.user_in_groups?('john', %w[bros], false), true) + + assert @ipa.user_in_groups?('john', %w[bros], false) end def test_is_some_groups service_bind basic_user - assert_equal(@ipa.user_in_groups?('john', %w[bros buds], false), true) + + assert @ipa.user_in_groups?('john', %w[bros buds], false) end - def test_is_in_all_groupss + def test_is_in_all_groups service_bind bigtime_user - assert_equal(true, @ipa.user_in_groups?('john', %w[broskies bros], true)) + + assert @ipa.user_in_groups?('john', %w[broskies bros], true) end def test_isnt_in_all_groups service_bind basic_user - assert_equal(@ipa.user_in_groups?('john', %w[bros buds], true), false) + + refute @ipa.user_in_groups?('john', %w[bros buds], true) end def test_isnt_in_groups service_bind basic_user - assert_equal(@ipa.user_in_groups?('john', %w[broskies], false), false) + + refute @ipa.user_in_groups?('john', %w[broskies], false) end def test_group_subset service_bind bigtime_user - assert_equal(@ipa.user_in_groups?('john', %w[broskies], true), true) + + assert @ipa.user_in_groups?('john', %w[broskies], true) end def test_user_exists - @md = MiniTest::Mock.new - @md.expect(:find_user, 'notnilluser', %w[john]) - @ipa.member_service = @md + md.expect(:find_user, 'notnilluser', %w[john]) + @ipa.member_service = md service_bind + assert(@ipa.user_exists?('john')) end def test_missing_user - @md = MiniTest::Mock.new - @md.expect(:find_user, nil) do |uid| - raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' + md.expect(:find_user, nil) do |uid| + uid != 'john' || raise(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) end - @ipa.member_service = @md + @ipa.member_service = md service_bind + refute(@ipa.user_exists?('john')) end def test_group_exists - @md = MiniTest::Mock.new - @md.expect(:find_group, 'notnillgroup', %w[broskies]) - @ipa.member_service = @md + md.expect(:find_group, 'notnillgroup', %w[broskies]) + @ipa.member_service = md service_bind + assert(@ipa.group_exists?('broskies')) end def test_missing_group - @md = MiniTest::Mock.new - @md.expect(:find_group, nil) do |gid| - raise LdapFluff::FreeIPA::MemberService::GIDNotFoundException if gid == 'broskies' + md.expect(:find_group, nil) do |gid| + gid != 'broskies' || raise(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) end - @ipa.member_service = @md + @ipa.member_service = md service_bind + refute(@ipa.group_exists?('broskies')) end - def test_find_users_in_nested_groups + def nested_groups group = Net::LDAP::Entry.new('gid=foremaners,cn=Groups,cn=accounts,dc=localdomain') group[:member] = ['gid=katellers,cn=Groups,cn=accounts,dc=localdomain'] + nested_group = Net::LDAP::Entry.new('gid=katellers,cn=Groups,cn=accounts,dc=localdomain') nested_group[:member] = ['uid=testuser,cn=users,cn=accounts,dc=localdomain'] - md = MiniTest::Mock.new - 2.times { md.expect(:find_group, [group], ['foremaners']) } - 2.times { md.expect(:find_group, [nested_group], ['katellers']) } + [group, nested_group] + end + + def basic_group(ret = nil, name = 'foremaners') + md.expect(:find_group, [ret], [name]) + md.expect(:find_group, ret, [name, false]) + end + + def test_find_users_in_nested_groups + group, nested_group = nested_groups + + basic_group(group) + basic_group(nested_group, 'katellers') 2.times { service_bind } - md.expect(:get_logins, ['testuser'], [['uid=testuser,cn=users,cn=accounts,dc=localdomain']]) + md.expect(:get_logins, ['testuser'], [['uid=testuser,cn=users,cn=accounts,dc=localdomain']]) @ipa.member_service = md - assert_equal @ipa.users_for_gid('foremaners'), ['testuser'] - md.verify + assert_equal ['testuser'], @ipa.users_for_gid('foremaners') end - end - diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 65d3f02..8a81c1b 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -7,55 +7,55 @@ class TestLDAP < MiniTest::Test def setup super - @fluff = LdapFluff.new(config_hash) + @fluff = LdapFluff.new(CONFIG_HASH) end def test_bind - @ldap.expect(:bind?, true, %w[john password]) - @fluff.ldap = @ldap - assert_equal(@fluff.authenticate?('john', 'password'), true) - @ldap.verify + ldap.expect(:bind?, true, %w[john password]) + @fluff.ldap = ldap + + assert @fluff.authenticate?('john', 'password') end def test_groups - @ldap.expect(:groups_for_uid, %w[bros], %w[john]) - @fluff.ldap = @ldap - assert_equal(@fluff.group_list('john'), %w[bros]) - @ldap.verify + ldap.expect(:groups_for_uid, %w[bros], %w[john]) + @fluff.ldap = ldap + + assert_equal %w[bros], @fluff.group_list('john') end def test_group_membership - @ldap.expect(:user_in_groups?, false, ['john', %w[broskies girlfriends], true]) - @fluff.ldap = @ldap - assert_equal(@fluff.user_in_groups?('john', %w[broskies girlfriends]), false) - @ldap.verify + ldap.expect(:user_in_groups?, false, ['john', %w[broskies girlfriends], true]) + @fluff.ldap = ldap + + refute @fluff.user_in_groups?('john', %w[broskies girlfriends]) end def test_valid_user - @ldap.expect(:user_exists?, true, %w[john]) - @fluff.ldap = @ldap + ldap.expect(:user_exists?, true, %w[john]) + @fluff.ldap = ldap + assert(@fluff.valid_user?('john')) - @ldap.verify end def test_valid_group - @ldap.expect(:group_exists?, true, %w[broskies]) - @fluff.ldap = @ldap + ldap.expect(:group_exists?, true, %w[broskies]) + @fluff.ldap = ldap + assert(@fluff.valid_group?('broskies')) - @ldap.verify end def test_invalid_group - @ldap.expect(:group_exists?, false, %w[broskerinos]) - @fluff.ldap = @ldap + ldap.expect(:group_exists?, false, %w[broskerinos]) + @fluff.ldap = ldap + refute(@fluff.valid_group?('broskerinos')) - @ldap.verify end def test_invalid_user - @ldap.expect(:user_exists?, false, ['johnny rotten']) - @fluff.ldap = @ldap + ldap.expect(:user_exists?, false, ['johnny rotten']) + @fluff.ldap = ldap + refute(@fluff.valid_user?('johnny rotten')) - @ldap.verify end end diff --git a/test/ldap_test_helper.rb b/test/ldap_test_helper.rb index a105324..dd553bc 100644 --- a/test/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -6,85 +6,97 @@ require 'minitest/autorun' module LdapTestHelper - def config_hash - { - host: 'internet.com', - port: 387, - encryption: :start_tls, - base_dn: 'dc=internet,dc=com', - group_base: 'ou=group,dc=internet,dc=com', - service_user: 'service', - service_pass: 'pass', - server_type: :free_ipa, - attr_login: nil, - search_filter: nil - } + CONFIG_HASH = { + host: 'internet.com', + port: 387, + encryption: :start_tls, + base_dn: 'dc=internet,dc=com', + group_base: 'ou=group,dc=internet,dc=com', + service_user: 'service', + service_pass: 'pass', + server_type: :free_ipa, + attr_login: nil, + search_filter: nil + }.freeze + + MOCK_VARS = %w[ldap md user_result].freeze + + # @!method ldap + # @return [MiniTest::Mock, Net::LDAP] + # @!method md + # @return [MiniTest::Mock, LdapFluff::GenericMemberService] + # @!method user_result + # @return [MiniTest::Mock, Net::LDAP::Entry] + MOCK_VARS.each do |var| + define_method(var.to_sym) do + instance_variable_get("@#{var}") || instance_variable_set("@#{var}", (v = MiniTest::Mock.new)) || v + end end def setup - config - @ldap = MiniTest::Mock.new + MOCK_VARS.each { |var| instance_variable_set("@#{var}", nil) } + end + + def teardown + MOCK_VARS.each { |var| (v = instance_variable_get("@#{var}")) && v.verify } end - def config - @config ||= LdapFluff::Config.new config_hash + # @return [LdapFluff::Config] + def config(extra = {}) + @config ||= LdapFluff::Config.new CONFIG_HASH.merge(extra) end + # @return [LdapFluff::Config] def netgroups_config - @config ||= LdapFluff::Config.new config_hash.merge(use_netgroups: true) + config(use_netgroups: true) end - def service_bind - @ldap.expect(:bind, true) - get_test_instance_variable.ldap = @ldap + # default setup for service bind users + def service_bind(user = 'service', pass = 'pass', ret = true) + ldap.expect(:auth, nil, [user, pass]) + ldap.expect(:bind, ret) + test_instance_variable.ldap = ldap end - def basic_user - @md = MiniTest::Mock.new - @md.expect(:find_user_groups, %w[bros], %w[john]) - get_test_instance_variable.member_service = @md + def basic_user(ret = %w[bros]) + md.expect(:find_user_groups, ret, %w[john]) + test_instance_variable.member_service = md end def bigtime_user - @md = MiniTest::Mock.new - @md.expect(:find_user_groups, %w[bros broskies], %w[john]) - get_test_instance_variable.member_service = @md + basic_user(%w[bros broskies]) end - def ad_name_filter(name) - Net::LDAP::Filter.eq('samaccountname', name) + def group_filter(cn) + Net::LDAP::Filter.eq('cn', cn) end - def ad_group_filter(name) - Net::LDAP::Filter.eq('cn', name) + def group_class_filter(name = 'group') + Net::LDAP::Filter.eq('objectClass', name) end - def ipa_name_filter(name) - Net::LDAP::Filter.eq('uid', name) + def ad_name_filter(name) + Net::LDAP::Filter.eq('samaccountname', name) end - def ipa_group_filter(name) - Net::LDAP::Filter.eq('cn', name) - end + alias ad_group_filter group_filter - def group_filter(g) - Net::LDAP::Filter.eq('cn', g) + def ipa_name_filter(name) + Net::LDAP::Filter.eq('uid', name) end - def group_class_filter - Net::LDAP::Filter.eq('objectclass', 'group') - end + alias ipa_group_filter group_filter def ipa_user_bind(uid) - "uid=#{uid},cn=users,cn=accounts,#{@config.base_dn}" + "uid=#{uid},cn=users,cn=accounts,#{config.base_dn}" end def ad_user_dn(name) - "CN=#{name},CN=Users,#{@config.base_dn}" + "CN=#{name},CN=Users,#{config.base_dn}" end def ad_group_dn(name = 'group') - "cn=#{name},#{@config.group_base}" + "cn=#{name},#{config.group_base}" end def ad_user_payload @@ -92,7 +104,7 @@ def ad_user_payload end def ad_group_payload - [{ cn: 'group', memberof: [ad_group_dn] }] + ad_user_payload.tap { |arr| arr[0].merge!(cn: 'group') } end def ad_parent_payload(num) @@ -103,12 +115,12 @@ def ad_double_payload(num) [{ memberof: [ad_group_dn("bros#{num}"), ad_group_dn("broskies#{num}")] }] end - def posix_user_payload - [{ cn: ['john'] }] + def posix_user_payload(name = 'john') + [{ cn: [name] }] end - def posix_group_payload - [{ cn: ['broze'] }] + def posix_group_payload(name = 'broze') + posix_user_payload(name) end def posix_netgroup_payload(cn, netgroups = []) @@ -116,28 +128,22 @@ def posix_netgroup_payload(cn, netgroups = []) end def ipa_user_payload - @ipa_user_payload_cache ||= begin - entry1 = Net::LDAP::Entry.new - entry1['cn'] = 'John' - - entry2 = Net::LDAP::Entry.new - entry2['memberof'] = ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] - - [entry1, entry2] - end + @ipa_user_payload ||= [ + Net::LDAP::Entry.new.tap { |e| e[:cn] = 'John' }, + Net::LDAP::Entry.new.tap { |e| e[:memberof] = %w[cn=group,dc=internet,dc=com cn=bros,dc=internet,dc=com] } + ] end def ipa_group_payload - [{ cn: 'group' }, { memberof: ['cn=group,dc=internet,dc=com', 'cn=bros,dc=internet,dc=com'] }] + [{ cn: 'group' }, { memberof: %w[cn=group,dc=internet,dc=com cn=bros,dc=internet,dc=com] }] end - def ipa_netgroup_payload(cn, netgroups = []) - [{ cn: [cn], nisnetgrouptriple: netgroups }] - end + alias ipa_netgroup_payload posix_netgroup_payload private - def get_test_instance_variable + # @return [LdapFluff::Generic] + def test_instance_variable instance_variable_get("@#{self.class.name.sub(/^Test|Test$/, '').downcase}") end end diff --git a/test/posix_member_services_test.rb b/test/posix_member_services_test.rb index a1581db..9148dbb 100644 --- a/test/posix_member_services_test.rb +++ b/test/posix_member_services_test.rb @@ -7,66 +7,60 @@ class TestPosixMemberService < MiniTest::Test def setup super - @ms = LdapFluff::Posix::MemberService.new(@ldap, @config) + # noinspection RubyYardParamTypeMatch + @ms = LdapFluff::Posix::MemberService.new(ldap, config) end def test_find_user user = posix_user_payload - @ldap.expect(:search, user, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap - assert_equal posix_user_payload, @ms.find_user('john') - @ldap.verify + ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + + assert_equal user.dup, @ms.find_user('john') end def test_find_user_groups user = posix_group_payload - @ldap.expect(:search, user, [filter: @ms.name_filter('john'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.group_base]) + @ms.ldap = ldap + assert_equal ['broze'], @ms.find_user_groups('john') - @ldap.verify end def test_find_no_groups - @ldap.expect(:search, [], [filter: @ms.name_filter('john'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, [], [filter: @ms.name_filter('john'), base: config.group_base]) + @ms.ldap = ldap + assert_equal [], @ms.find_user_groups('john') - @ldap.verify end def test_user_exists user = posix_user_payload - @ldap.expect(:search, user, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap + ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + assert @ms.find_user('john') - @ldap.verify end def test_user_doesnt_exists - @ldap.expect(:search, nil, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap + ldap.expect(:search, nil, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } - @ldap.verify end def test_group_exists group = posix_group_payload - @ldap.expect(:search, group, [filter: @ms.group_filter('broze'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, group, [filter: @ms.group_filter('broze'), base: config.group_base]) + @ms.ldap = ldap + assert @ms.find_group('broze') - @ldap.verify end def test_group_doesnt_exists - @ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), base: config.group_base]) + @ms.ldap = ldap + assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } - @ldap.verify end end diff --git a/test/posix_netgroup_member_services_test.rb b/test/posix_netgroup_member_services_test.rb index a767f2f..653298c 100644 --- a/test/posix_netgroup_member_services_test.rb +++ b/test/posix_netgroup_member_services_test.rb @@ -6,71 +6,62 @@ class TestPosixNetgroupMemberService < MiniTest::Test include LdapTestHelper def setup - netgroups_config super - @ms = LdapFluff::Posix::NetgroupMemberService.new(@ldap, netgroups_config) + # noinspection RubyYardParamTypeMatch + @ms = LdapFluff::Posix::NetgroupMemberService.new(ldap, netgroups_config) end def test_find_user user = posix_user_payload - @ldap.expect(:search, user, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap - assert_equal posix_user_payload, @ms.find_user('john') - @ldap.verify + ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + + assert_equal user.dup, @ms.find_user('john') end def test_find_user_groups - response = posix_netgroup_payload('bros', ['(,john,)', '(,joe,)']) - @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - base: config.group_base]) + response = posix_netgroup_payload('bros', %w[(,john,) (,joe,)]) + ldap.expect(:search, response, [filter: group_class_filter('nisNetgroup'), base: config.group_base]) + @ms.ldap = ldap - @ms.ldap = @ldap assert_equal ['bros'], @ms.find_user_groups('john') - @ldap.verify end def test_find_no_user_groups response = posix_netgroup_payload('bros', ['(,joe,)']) - @ldap.expect(:search, response, [filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), - base: config.group_base]) + ldap.expect(:search, response, [filter: group_class_filter('nisNetgroup'), base: config.group_base]) + @ms.ldap = ldap - @ms.ldap = @ldap assert_equal [], @ms.find_user_groups('john') - @ldap.verify end def test_user_exists user = posix_user_payload - @ldap.expect(:search, user, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap + ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + assert @ms.find_user('john') - @ldap.verify end def test_user_doesnt_exists - @ldap.expect(:search, nil, [filter: @ms.name_filter('john'), - base: config.base_dn]) - @ms.ldap = @ldap + ldap.expect(:search, nil, [filter: @ms.name_filter('john'), base: config.base_dn]) + @ms.ldap = ldap + assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } - @ldap.verify end def test_group_exists group = posix_netgroup_payload('broze') - @ldap.expect(:search, group, [filter: @ms.group_filter('broze'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, group, [filter: @ms.group_filter('broze'), base: config.group_base]) + @ms.ldap = ldap + assert @ms.find_group('broze') - @ldap.verify end def test_group_doesnt_exists - @ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), - base: config.group_base]) - @ms.ldap = @ldap + ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), base: config.group_base]) + @ms.ldap = ldap + assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } - @ldap.verify end end diff --git a/test/posix_test.rb b/test/posix_test.rb index 8c526bb..4f40351 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -7,136 +7,143 @@ class TestPosix < MiniTest::Test def setup super - @posix = LdapFluff::Posix.new(@config) - end - - def service_bind - @ldap.expect(:auth, nil, %w[service pass]) - super + @posix = LdapFluff::Posix.new(config) end def test_groups service_bind basic_user - assert_equal(@posix.groups_for_uid('john'), %w[bros]) + + assert_equal %w[bros], @posix.groups_for_uid('john') end def test_missing_user - md = MiniTest::Mock.new + service_bind + md.expect(:find_user_groups, [], %w[john]) @posix.member_service = md + assert_equal([], @posix.groups_for_uid('john')) end def test_isnt_in_groups service_bind basic_user - assert_equal(@posix.user_in_groups?('john', %w[broskies], true), false) + + refute @posix.user_in_groups?('john', %w[broskies], true) end def test_is_in_groups service_bind basic_user - assert_equal(@posix.user_in_groups?('john', %w[bros], true), true) + + assert @posix.user_in_groups?('john', %w[bros], true) end def test_is_in_no_groups service_bind - basic_user - assert_equal(@posix.user_in_groups?('john', [], true), true) + # basic_user + + assert @posix.user_in_groups?('john', [], true) end + # looks up the uid's full DN via the service account def test_good_bind - # looks up the uid's full DN via the service account - @md = MiniTest::Mock.new - user_result = MiniTest::Mock.new user_result.expect(:dn, 'uid=internet,dn=example') - @md.expect(:find_user, [user_result], %w[internet]) - @posix.member_service = @md + md.expect(:find_user, user_result, ['internet', true]) + @posix.member_service = md + service_bind - @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) - @ldap.expect(:bind, true) - @posix.ldap = @ldap - assert_equal(@posix.bind?('internet', 'password'), true) + service_bind('uid=internet,dn=example', 'password') + + assert @posix.bind?('internet', 'password') end def test_good_bind_with_dn # no expectation on the service account - @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) - @ldap.expect(:bind, true) - @posix.ldap = @ldap - assert_equal(@posix.bind?('uid=internet,dn=example', 'password'), true) + service_bind('uid=internet,dn=example', 'password') + + assert @posix.bind?('uid=internet,dn=example', 'password') end def test_bad_bind - @ldap.expect(:auth, nil, %w[uid=internet,dn=example password]) - @ldap.expect(:bind, false) - @posix.ldap = @ldap - assert_equal(@posix.bind?('uid=internet,dn=example', 'password'), false) + service_bind('uid=internet,dn=example', 'password', false) + + refute @posix.bind?('uid=internet,dn=example', 'password') end def test_user_exists service_bind - md = MiniTest::Mock.new + md.expect(:find_user, 'notnilluser', %w[john]) @posix.member_service = md + assert(@posix.user_exists?('john')) end - def test_missing_user + def test_user_not_exists service_bind - md = MiniTest::Mock.new + md.expect(:find_user, nil) do |uid| - raise LdapFluff::Posix::MemberService::UIDNotFoundException if uid == 'john' + uid != 'john' || raise(LdapFluff::Posix::MemberService::UIDNotFoundException) end @posix.member_service = md + refute(@posix.user_exists?('john')) end def test_group_exists service_bind - md = MiniTest::Mock.new + md.expect(:find_group, 'notnillgroup', %w[broskies]) @posix.member_service = md + assert(@posix.group_exists?('broskies')) end def test_missing_group service_bind - md = MiniTest::Mock.new + md.expect(:find_group, nil) do |gid| - raise LdapFluff::Posix::MemberService::GIDNotFoundException if gid == 'broskies' + gid != 'broskies' || raise(LdapFluff::Posix::MemberService::GIDNotFoundException) end @posix.member_service = md + refute(@posix.group_exists?('broskies')) end - def test_find_users_in_nested_groups - service_bind + def nested_groups group = Net::LDAP::Entry.new('CN=foremaners,DC=example,DC=com') group[:memberuid] = ['katellers'] + nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com') nested_group[:memberuid] = ['testuser'] - @ldap.expect(:search, [nested_group], [base: group.dn, filter: groups_filter]) - @posix.ldap = @ldap - - md = MiniTest::Mock.new - 2.times { md.expect(:find_group, [group], ['foremaners']) } - @posix.member_service = md + [group, nested_group] + end - assert_equal @posix.users_for_gid('foremaners'), ['testuser'] + def group_class_filter + super('posixGroup') | + super('organizationalunit') | + super('groupOfUniqueNames') | + super('groupOfNames') + end - md.verify - @ldap.verify + def basic_group(ret = nil, name = 'foremaners') + md.expect(:find_group, [ret], [name]) + md.expect(:find_group, ret, [name, false]) end - private + def test_find_users_in_nested_groups + service_bind + group, nested_group = nested_groups + + ldap.expect(:search, [nested_group], [base: group.dn, filter: group_class_filter]) + @posix.ldap = ldap + + basic_group(group) + @posix.member_service = md - def groups_filter - Net::LDAP::Filter.eq('objectClass', 'posixGroup') | - Net::LDAP::Filter.eq('objectClass', 'organizationalunit') | - Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') | - Net::LDAP::Filter.eq('objectClass', 'groupOfNames') + assert_equal ['testuser'], @posix.users_for_gid('foremaners') end end From 6d30c6fdcbe7de4686cdf02cee1b1e66c120fd4b Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Sat, 16 Nov 2019 11:47:31 +0700 Subject: [PATCH 12/15] - Use base method `*_filter` to create `Net::LDAP::Filter` - Add more tests to cover almost logic cases - Optimize method `users_for_gid` to call `find_group` one-times only --- lib/ldap_fluff/ad_member_service.rb | 2 +- lib/ldap_fluff/freeipa_member_service.rb | 6 +- .../freeipa_netgroup_member_service.rb | 2 +- lib/ldap_fluff/generic.rb | 29 ++++--- lib/ldap_fluff/generic_member_service.rb | 24 ++++-- lib/ldap_fluff/posix_member_service.rb | 4 +- .../posix_netgroup_member_service.rb | 2 +- test/ad_test.rb | 68 ++++++++++------ test/config_test.rb | 44 ++++++++++- test/ipa_member_services_test.rb | 11 +++ test/ipa_test.rb | 36 ++++++--- test/ldap_test.rb | 55 +++++++++++++ test/ldap_test_helper.rb | 15 ++-- test/posix_member_services_test.rb | 27 ++++--- test/posix_netgroup_member_services_test.rb | 10 +-- test/posix_test.rb | 79 ++++++++++++++----- 16 files changed, 307 insertions(+), 107 deletions(-) diff --git a/lib/ldap_fluff/ad_member_service.rb b/lib/ldap_fluff/ad_member_service.rb index 080a9bf..d61c38c 100644 --- a/lib/ldap_fluff/ad_member_service.rb +++ b/lib/ldap_fluff/ad_member_service.rb @@ -3,7 +3,7 @@ # Naughty bits of active directory LDAP queries class LdapFluff::ActiveDirectory::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap - # @param [Config] config + # @param [LdapFluff::Config] config def initialize(ldap, config) config.instance_variable_set(:@attr_login, 'samaccountname') unless config.attr_login super diff --git a/lib/ldap_fluff/freeipa_member_service.rb b/lib/ldap_fluff/freeipa_member_service.rb index a3f69bf..eaa8e08 100644 --- a/lib/ldap_fluff/freeipa_member_service.rb +++ b/lib/ldap_fluff/freeipa_member_service.rb @@ -2,7 +2,7 @@ class LdapFluff::FreeIPA::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap - # @param [Config] config + # @param [LdapFluff::Config] config def initialize(ldap, config) config.instance_variable_set(:@attr_login, 'uid') unless config.attr_login super @@ -26,11 +26,11 @@ def find_user_groups(uid) # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) grouplist.map do |g| - if g =~ /.*?ipauniqueid=(.*?)/i + if g =~ /.*?\bipauniqueid=(.*?)/i search = (ldap.search(base: g.downcase) || []).first search ? search[:cn].first : nil else - g.downcase.sub(/.*?cn=(.*?),.*/, '\1') + g.downcase.sub(/.*?\bcn=(.*?),.*/, '\1') end end.compact end diff --git a/lib/ldap_fluff/freeipa_netgroup_member_service.rb b/lib/ldap_fluff/freeipa_netgroup_member_service.rb index 23b5258..eec2790 100644 --- a/lib/ldap_fluff/freeipa_netgroup_member_service.rb +++ b/lib/ldap_fluff/freeipa_netgroup_member_service.rb @@ -4,7 +4,7 @@ class LdapFluff::FreeIPA::NetgroupMemberService < LdapFluff::FreeIPA::MemberServ # @param [String] uid # @return [Array] def find_user_groups(uid) - groups = ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: config.group_base) + groups = ldap.search(filter: class_filter('nisNetgroup'), base: config.group_base) return [] unless groups groups.map do |entry| diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index 8d5a129..53cfb45 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -5,13 +5,13 @@ class LdapFluff::Generic # @!attribute [rw] ldap # @return [Net::LDAP] # @!attribute [rw] member_service - # @return [GenericMemberService] + # @return [LdapFluff::GenericMemberService] attr_accessor :ldap, :member_service - # @return [Config] + # @return [LdapFluff::Config] attr_reader :config - # @param [Config] config + # @param [LdapFluff::Config] config def initialize(config) @config = config @@ -51,11 +51,15 @@ def groups_for_uid(uid) # @param [String] gid # @return [Array] def users_for_gid(gid) - return [] unless group_exists?(gid) + service_bind + begin + # @type [Net::LDAP::Entry] + search = member_service.find_group(gid, false) + rescue self.class::MemberService::GIDNotFoundException + return [] + end - search = member_service.find_group(gid, false) method = select_member_method(search) - method ? users_from_search_results(search, method) : [] end @@ -79,11 +83,12 @@ def user_in_groups?(uid, gids = [], all = true) # @param [String] cn # @return [Boolean] + # @deprecated def includes_cn?(cn) - filter = Net::LDAP::Filter.eq('cn', cn) - result = ldap.search(base: ldap.base, filter: filter) + service_bind + search = ldap.search(filter: Net::LDAP::Filter.eq('cn', cn)) # NOTE: present? - !(result.respond_to?(:empty?) ? result.empty? : !result) + !(search.respond_to?(:empty?) ? search.empty? : !search) end # @raise [UnauthenticatedException] @@ -116,7 +121,7 @@ def select_member_method(search_result) end end - # @param [Config] config + # @param [LdapFluff::Config] config # @return [Net::LDAP] def create_ldap_client(config) Net::LDAP.new( @@ -128,8 +133,8 @@ def create_ldap_client(config) ) end - # @param [Config] config - # @return [GenericMemberService] + # @param [LdapFluff::Config] config + # @return [LdapFluff::GenericMemberService] def create_member_service(config) if config.use_netgroups self.class::NetgroupMemberService.new(@ldap, config) diff --git a/lib/ldap_fluff/generic_member_service.rb b/lib/ldap_fluff/generic_member_service.rb index d342d7d..51e23dd 100644 --- a/lib/ldap_fluff/generic_member_service.rb +++ b/lib/ldap_fluff/generic_member_service.rb @@ -6,13 +6,13 @@ class LdapFluff::GenericMemberService attr_accessor :ldap # @!attribute [r] config - # @return [Config] + # @return [LdapFluff::Config] # @!attribute [r] search_filter # @return [Net::LDAP::Filter] attr_reader :config, :search_filter # @param [Net::LDAP] ldap - # @param [Config] config + # @param [LdapFluff::Config] config def initialize(ldap, config) @ldap = ldap @config = config @@ -33,6 +33,8 @@ def try_create_filter(filter, kind = :Search) # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] def find_user(uid, only = nil) + return find_by_dn(uid, only) if uid.include?(',') + # @type [Array] user = ldap.search(filter: name_filter(uid)) raise self.class::UIDNotFoundException if !user || user.empty? @@ -60,6 +62,8 @@ def find_by_dn(dn, only = nil) # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] def find_group(gid, only = nil) + return find_by_dn(gid, only) if gid.include?(',') + # @type [Array] group = ldap.search(filter: group_filter(gid), base: config.group_base) raise self.class::GIDNotFoundException if !group || group.empty? @@ -87,11 +91,17 @@ def group_filter(gid) Net::LDAP::Filter.eq('cn', gid) end + # @param [String] name + # @return [Net::LDAP::Filter] + def class_filter(name) + Net::LDAP::Filter.eq('objectClass', name) + end + # extract the group names from the LDAP style response, # @param [Array] grouplist # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) - grouplist.map { |g| g.downcase.sub(/.*?cn=(.*?),.*/, '\1') } + grouplist.map { |g| g.downcase.sub(/.*?\bcn=(.*?),.*/, '\1') } end # @param [Array] netgroup_triples @@ -107,12 +117,10 @@ def get_netgroup_users(netgroup_triples) def get_logins(userlist) userlist.map!(&:downcase) - results = [config.attr_login, 'uid', 'cn'].map do |attribute| - logins = userlist.map { |g| g.sub(/.*?#{attribute}=(.*?),.*/, '\1') } + [config.attr_login, 'uid', 'cn'].map do |attribute| + logins = userlist.map { |g| g.sub(/.*?\b#{attribute}=(.*?),.*/, '\1') } logins == userlist ? nil : logins - end - - results.flatten.compact.uniq + end.flatten.compact.uniq end # @param [Net::LDAP::Entry] entry diff --git a/lib/ldap_fluff/posix_member_service.rb b/lib/ldap_fluff/posix_member_service.rb index eec93d2..5637dee 100644 --- a/lib/ldap_fluff/posix_member_service.rb +++ b/lib/ldap_fluff/posix_member_service.rb @@ -3,7 +3,7 @@ # handles the naughty bits of POSIX LDAP class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap - # @param [Config] config + # @param [LdapFluff::Config] config def initialize(ldap, config) config.instance_variable_set(:@attr_login, 'memberuid') unless config.attr_login super @@ -31,7 +31,7 @@ def find_user(uid, only = nil, base_dn = nil) # @return [Array] an LDAP user with groups attached # @note this method is not particularly fast for large LDAP systems def find_user_groups(uid) - groups = ldap.search(filter: Net::LDAP::Filter.eq('memberuid', uid), base: config.group_base) + groups = ldap.search(filter: name_filter(uid), base: config.group_base) return [] unless groups groups.map { |entry| entry[:cn].first } diff --git a/lib/ldap_fluff/posix_netgroup_member_service.rb b/lib/ldap_fluff/posix_netgroup_member_service.rb index 98b8aee..335fbb3 100644 --- a/lib/ldap_fluff/posix_netgroup_member_service.rb +++ b/lib/ldap_fluff/posix_netgroup_member_service.rb @@ -5,7 +5,7 @@ class LdapFluff::Posix::NetgroupMemberService < LdapFluff::Posix::MemberService # @param [String] uid # @return [Array] list of group CNs for a user def find_user_groups(uid) - groups = ldap.search(filter: Net::LDAP::Filter.eq('objectClass', 'nisNetgroup'), base: config.group_base) + groups = ldap.search(filter: class_filter('nisNetgroup'), base: config.group_base) return [] unless groups groups.map do |entry| diff --git a/test/ad_test.rb b/test/ad_test.rb index ab354b8..098521a 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -53,7 +53,7 @@ def test_bad_user service_bind md.expect(:find_user_groups, nil) do |uid| - uid != 'john' || raise(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' end @ad.member_service = md @@ -103,16 +103,11 @@ def test_group_subset assert @ad.user_in_groups?('john', %w[broskies], true) end - def basic_group(ret = nil, name = 'foremaners') - md.expect(:find_group, [ret], [name]) - md.expect(:find_group, ret, [name, false]) - end - def test_subgroups_in_groups_are_ignored - group = Net::LDAP::Entry.new('foremaners') - basic_group(group) - service_bind # 2.times + service_bind + group = Net::LDAP::Entry.new('foremaners') + md.expect(:find_group, group, ['foremaners', false]) # NOTE: md.expect(:find_by_dn, nil) { raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException } @ad.member_service = md @@ -130,7 +125,7 @@ def test_user_exists def test_missing_user md.expect(:find_user, nil) do |uid| - uid != 'john' || raise(LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException) + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' end @ad.member_service = md @@ -150,7 +145,7 @@ def test_group_exists def test_missing_group md.expect(:find_group, nil) do |gid| - gid != 'broskies' || raise(LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException) + raise LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException if gid == 'broskies' end @ad.member_service = md @@ -174,16 +169,21 @@ def nested_groups [group, nested_group, nested_user] end + def bind_nested_groups(group, nested_group) + md.expect(:find_group, group, ['foremaners', false]) + md.expect(:find_group, nested_group, ['katellers', false]) + @ad.member_service = md + + 2.times { service_bind } + end + def test_find_users_in_nested_groups group, nested_group, nested_user = nested_groups - basic_group(group) - basic_group(nested_group, 'katellers') - 2.times { service_bind } + bind_nested_groups(group, nested_group) - md.expect(:find_by_dn, nested_group, ['CN=katellers,DC=corp,DC=windows,DC=com', true]) - md.expect(:find_by_dn, nested_user, ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', true]) + md.expect(:find_by_dn, nested_group, [group[:member].first, true]) + md.expect(:find_by_dn, nested_user, [nested_group[:member].first, true]) md.expect(:get_login_from_entry, 'testuser', [nested_user]) - @ad.member_service = md assert_equal ['testuser'], @ad.users_for_gid('foremaners') end @@ -205,15 +205,37 @@ def empty_nested_groups def test_find_users_with_empty_nested_group group, nested_group, nested_user = empty_nested_groups - basic_group(group) - basic_group(nested_group, 'katellers') - 2.times { service_bind } + bind_nested_groups(group, nested_group) - md.expect(:find_by_dn, nested_user, ['CN=Test User,CN=Users,DC=corp,DC=windows,DC=com', true]) - md.expect(:find_by_dn, nested_group, ['CN=katellers,DC=corp,DC=windows,DC=com', true]) + md.expect(:find_by_dn, nested_user, [group[:member].first, true]) + md.expect(:find_by_dn, nested_group, [group[:member].last, true]) md.expect(:get_login_from_entry, 'testuser', [nested_user]) - @ad.member_service = md assert_equal ['testuser'], @ad.users_for_gid('foremaners') end + + def test_non_exist_user_in_groups + service_bind + + md.expect(:find_user_groups, nil) do |uid| + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if uid == 'john' + end + @ad.member_service = md + + refute @ad.user_in_groups?('john', [nil]) + end + + def test_invalid_users_for_group + service_bind + + group = Net::LDAP::Entry.new('foremaners').tap { |g| g[:uniquemember] = ['testuser'] } + md.expect(:find_group, group, ['foremaners', false]) + + md.expect(:find_by_dn, nil) do |dn, only| + raise LdapFluff::ActiveDirectory::MemberService::UIDNotFoundException if dn == 'testuser' && only + end + @ad.member_service = md + + assert_equal [], @ad.users_for_gid('foremaners') + end end diff --git a/test/config_test.rb b/test/config_test.rb index ee8d616..aee3219 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -6,9 +6,7 @@ class ConfigTest < MiniTest::Test include LdapTestHelper def test_unsupported_type - assert_raises(LdapFluff::Config::ConfigError) do - LdapFluff.new CONFIG_HASH.merge(server_type: 'inactive_directory') - end + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new CONFIG_HASH.merge(server_type: 'inactive_directory') } end def test_load_posix @@ -31,4 +29,44 @@ def test_instrumentation_service net_ldap = LdapFluff.new(CONFIG_HASH.merge(instrumentation_service: is)).ldap.ldap assert_equal is, net_ldap.send(:instrumentation_service) end + + def test_missing_keys + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new(CONFIG_HASH.reject { |_, v| v.nil? }) } + end + + def test_unknown_keys + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new CONFIG_HASH.merge(unknown_key: nil) } + end + + def test_anon_queries + fluff = LdapFluff.new CONFIG_HASH.merge('anon_queries' => true, service_user: nil) + assert fluff.ldap.user_in_groups?(nil) + end + + def test_nil_required_keys + %w[host port base_dn server_type service_user service_pass].each do |key| + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new CONFIG_HASH.merge(key => nil) } + end + end + + def test_invalid_anon_queries_set + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new CONFIG_HASH.merge(anon_queries: 0) } + end + + def test_nil_group_base + fluff = LdapFluff.new CONFIG_HASH.merge('group_base' => nil) + assert_equal fluff.ldap.config.base_dn, fluff.ldap.config.group_base + end + + def test_load_posix_netgroup + fluff = LdapFluff.new CONFIG_HASH.merge('server_type' => :posix, use_netgroups: 1) + assert_instance_of LdapFluff::Posix::NetgroupMemberService, fluff.ldap.member_service + end + + def test_bad_search_filter + assert_output(nil, /\bSearch filter unavailable\b/) do + fluff = LdapFluff.new CONFIG_HASH.merge('search_filter' => 'bad-filter') + assert_nil fluff.ldap.member_service.search_filter + end + end end diff --git a/test/ipa_member_services_test.rb b/test/ipa_member_services_test.rb index 98f5247..3875b7e 100644 --- a/test/ipa_member_services_test.rb +++ b/test/ipa_member_services_test.rb @@ -75,4 +75,15 @@ def test_find_missing_group @ipams.find_group('broze') end end + + def test_ipa_unique_groups + user = Net::LDAP::Entry.new.tap { |e| e[:memberof] = %w[cn=group,dc ipauniqueid=bros] } + ldap.expect(:search, [nil, user], [filter: ipa_name_filter('john')]) + + entry = Net::LDAP::Entry.new.tap { |e| e[:cn] = 'broze' } + ldap.expect(:search, [entry], [base: user[:memberof].last]) + @ipams.ldap = ldap + + assert_equal %w[group broze], @ipams.find_user_groups('john') + end end diff --git a/test/ipa_test.rb b/test/ipa_test.rb index 4ac0f02..1bca2e0 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -51,7 +51,7 @@ def test_bad_user service_bind md.expect(:find_user_groups, nil) do |uid| - uid != 'john' || raise(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) + raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' end @ipa.member_service = md @@ -118,7 +118,7 @@ def test_user_exists def test_missing_user md.expect(:find_user, nil) do |uid| - uid != 'john' || raise(LdapFluff::FreeIPA::MemberService::UIDNotFoundException) + raise LdapFluff::FreeIPA::MemberService::UIDNotFoundException if uid == 'john' end @ipa.member_service = md service_bind @@ -136,7 +136,7 @@ def test_group_exists def test_missing_group md.expect(:find_group, nil) do |gid| - gid != 'broskies' || raise(LdapFluff::FreeIPA::MemberService::GIDNotFoundException) + raise LdapFluff::FreeIPA::MemberService::GIDNotFoundException if gid == 'broskies' end @ipa.member_service = md service_bind @@ -148,27 +148,39 @@ def nested_groups group = Net::LDAP::Entry.new('gid=foremaners,cn=Groups,cn=accounts,dc=localdomain') group[:member] = ['gid=katellers,cn=Groups,cn=accounts,dc=localdomain'] - nested_group = Net::LDAP::Entry.new('gid=katellers,cn=Groups,cn=accounts,dc=localdomain') + nested_group = Net::LDAP::Entry.new(group[:member].first) nested_group[:member] = ['uid=testuser,cn=users,cn=accounts,dc=localdomain'] [group, nested_group] end - def basic_group(ret = nil, name = 'foremaners') - md.expect(:find_group, [ret], [name]) - md.expect(:find_group, ret, [name, false]) - end - def test_find_users_in_nested_groups group, nested_group = nested_groups - basic_group(group) - basic_group(nested_group, 'katellers') + md.expect(:find_group, group, ['foremaners', false]) + md.expect(:find_group, nested_group, ['katellers', false]) 2.times { service_bind } - md.expect(:get_logins, ['testuser'], [['uid=testuser,cn=users,cn=accounts,dc=localdomain']]) + md.expect(:get_logins, ['testuser'], [nested_group[:member]]) @ipa.member_service = md assert_equal ['testuser'], @ipa.users_for_gid('foremaners') end + + def test_insufficient_privileges_user + @ipa.member_service.ldap = service_bind + ldap.expect(:search, [nil], [filter: ipa_name_filter('john')]) + + assert_raises(LdapFluff::FreeIPA::UnauthenticatedException) { @ipa.groups_for_uid('john') } + end + + def test_find_users_for_netgroup + config.instance_variable_set(:@use_netgroups, true) + @ipa.member_service.ldap = service_bind + + group = Net::LDAP::Entry.new('gid=foremaners').tap { |g| g[:nisnetgrouptriple] = %w[(,john,) (,joe,)] } + ldap.expect(:search, [group], [filter: ipa_group_filter('foremaners'), base: config.group_base]) + + assert_equal %w[john joe], @ipa.users_for_gid('foremaners') + end end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 8a81c1b..e19f53c 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -58,4 +58,59 @@ def test_invalid_user refute(@fluff.valid_user?('johnny rotten')) end + + def test_unknown_server_type + @fluff.ldap.config.instance_variable_set(:@server_type, nil) + assert_raises(RuntimeError) { @fluff.send(:create_provider, @fluff.ldap.config) } + end + + def test_instrument + md.expect(:instrument, ret = nil) do |event, payload, &blk| + if event == 'test.ldap_fluff' && payload == {} + blk.call(payload) + payload.key?(:result) + end + end + @fluff.instrumentation_service = md + + ldap.expect(:open, ret) + @fluff.ldap.ldap = ldap + + assert_nil @fluff.test + end + + def test_user_list + ldap.expect(:users_for_gid, %w[john], %w[bros]) + @fluff.ldap = ldap + + assert_equal %w[john], @fluff.user_list('bros') + end + + def test_found_user + md.expect(:find_user, user = Object.new, ['john', true]) + @fluff.ldap.member_service = md + + assert_equal user, @fluff.find_user('john', true) + end + + def test_found_group + md.expect(:find_group, group = [Object.new], ['bros', nil]) + @fluff.ldap.member_service = md + + assert_equal group, @fluff.find_group('bros') + end + + # @deprecated + def test_includes_cn + service_bind ipa_user_bind('service') + ldap.expect(:search, [nil], [filter: group_filter('bros')]) + + assert @fluff.ldap.send(:includes_cn?, 'bros') + end + + private + + def test_instance_variable + @fluff.ldap + end end diff --git a/test/ldap_test_helper.rb b/test/ldap_test_helper.rb index dd553bc..8d4bda0 100644 --- a/test/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -75,16 +75,17 @@ def group_class_filter(name = 'group') Net::LDAP::Filter.eq('objectClass', name) end - def ad_name_filter(name) - Net::LDAP::Filter.eq('samaccountname', name) + # @!method ad_name_filter + # @return [Net::LDAP::Filter] + # @!method posix_name_filter + # @return [Net::LDAP::Filter] + # @!method ipa_name_filter + # @return [Net::LDAP::Filter] + { ad: 'samaccountname', posix: 'memberuid', ipa: 'uid' }.each do |key, attr| + define_method("#{key}_name_filter".to_sym) { |name| Net::LDAP::Filter.eq(attr, name) } end alias ad_group_filter group_filter - - def ipa_name_filter(name) - Net::LDAP::Filter.eq('uid', name) - end - alias ipa_group_filter group_filter def ipa_user_bind(uid) diff --git a/test/posix_member_services_test.rb b/test/posix_member_services_test.rb index 9148dbb..3df6ba8 100644 --- a/test/posix_member_services_test.rb +++ b/test/posix_member_services_test.rb @@ -13,22 +13,22 @@ def setup def test_find_user user = posix_user_payload - ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap - assert_equal user.dup, @ms.find_user('john') + assert_equal user.dup, @ms.find_user('john', config.base_dn) end def test_find_user_groups user = posix_group_payload - ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.group_base]) + ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.group_base]) @ms.ldap = ldap assert_equal ['broze'], @ms.find_user_groups('john') end def test_find_no_groups - ldap.expect(:search, [], [filter: @ms.name_filter('john'), base: config.group_base]) + ldap.expect(:search, [], [filter: posix_name_filter('john'), base: config.group_base]) @ms.ldap = ldap assert_equal [], @ms.find_user_groups('john') @@ -36,14 +36,14 @@ def test_find_no_groups def test_user_exists user = posix_user_payload - ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap - assert @ms.find_user('john') + assert_equal user.last.dup, @ms.find_user('john', false) end def test_user_doesnt_exists - ldap.expect(:search, nil, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, nil, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } @@ -51,16 +51,25 @@ def test_user_doesnt_exists def test_group_exists group = posix_group_payload - ldap.expect(:search, group, [filter: @ms.group_filter('broze'), base: config.group_base]) + ldap.expect(:search, group, [filter: group_filter('broze'), base: config.group_base]) @ms.ldap = ldap assert @ms.find_group('broze') end def test_group_doesnt_exists - ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), base: config.group_base]) + ldap.expect(:search, nil, [filter: group_filter('broze'), base: config.group_base]) @ms.ldap = ldap assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } end + + # @deprecated + def test_times_in_groups + filter = posix_name_filter('uid') & (group_filter('gid') | group_filter('s')) + ldap.expect(:search, nil, [base: config.group_base, filter: filter]) + @ms.ldap = ldap + + assert_equal 0, @ms.send(:times_in_groups, 'uid', %w[gid s]) + end end diff --git a/test/posix_netgroup_member_services_test.rb b/test/posix_netgroup_member_services_test.rb index 653298c..7d67e10 100644 --- a/test/posix_netgroup_member_services_test.rb +++ b/test/posix_netgroup_member_services_test.rb @@ -13,7 +13,7 @@ def setup def test_find_user user = posix_user_payload - ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap assert_equal user.dup, @ms.find_user('john') @@ -37,14 +37,14 @@ def test_find_no_user_groups def test_user_exists user = posix_user_payload - ldap.expect(:search, user, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap assert @ms.find_user('john') end def test_user_doesnt_exists - ldap.expect(:search, nil, [filter: @ms.name_filter('john'), base: config.base_dn]) + ldap.expect(:search, nil, [filter: posix_name_filter('john'), base: config.base_dn]) @ms.ldap = ldap assert_raises(LdapFluff::Posix::MemberService::UIDNotFoundException) { @ms.find_user('john') } @@ -52,14 +52,14 @@ def test_user_doesnt_exists def test_group_exists group = posix_netgroup_payload('broze') - ldap.expect(:search, group, [filter: @ms.group_filter('broze'), base: config.group_base]) + ldap.expect(:search, group, [filter: group_filter('broze'), base: config.group_base]) @ms.ldap = ldap assert @ms.find_group('broze') end def test_group_doesnt_exists - ldap.expect(:search, nil, [filter: @ms.group_filter('broze'), base: config.group_base]) + ldap.expect(:search, nil, [filter: group_filter('broze'), base: config.group_base]) @ms.ldap = ldap assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } diff --git a/test/posix_test.rb b/test/posix_test.rb index 4f40351..13699e1 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -81,11 +81,11 @@ def test_user_exists assert(@posix.user_exists?('john')) end - def test_user_not_exists + def test_user_doesnt_exists service_bind md.expect(:find_user, nil) do |uid| - uid != 'john' || raise(LdapFluff::Posix::MemberService::UIDNotFoundException) + raise LdapFluff::Posix::MemberService::UIDNotFoundException if uid == 'john' end @posix.member_service = md @@ -105,45 +105,84 @@ def test_missing_group service_bind md.expect(:find_group, nil) do |gid| - gid != 'broskies' || raise(LdapFluff::Posix::MemberService::GIDNotFoundException) + raise LdapFluff::Posix::MemberService::GIDNotFoundException if gid == 'broskies' end @posix.member_service = md refute(@posix.group_exists?('broskies')) end - def nested_groups + def posix_groups_filter + group_class_filter('posixGroup') | + group_class_filter('organizationalunit') | + group_class_filter('groupOfUniqueNames') | + group_class_filter('groupOfNames') + end + + def bind_nested_groups(attr = :memberuid) + service_bind + group = Net::LDAP::Entry.new('CN=foremaners,DC=example,DC=com') - group[:memberuid] = ['katellers'] + group[attr] = ['katellers'] nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com') - nested_group[:memberuid] = ['testuser'] + nested_group[attr] = [attr == :member ? 'memberuid=testuser,' : 'testuser'] [group, nested_group] end - def group_class_filter - super('posixGroup') | - super('organizationalunit') | - super('groupOfUniqueNames') | - super('groupOfNames') + def test_find_users_in_nested_groups + group, nested_group = bind_nested_groups + ldap.expect(:search, [nested_group], [base: group.dn, filter: posix_groups_filter]) + + md.expect(:find_group, group, ['foremaners', false]) + @posix.member_service = md + + assert_equal ['testuser'], @posix.users_for_gid('foremaners') end - def basic_group(ret = nil, name = 'foremaners') - md.expect(:find_group, [ret], [name]) - md.expect(:find_group, ret, [name, false]) + def test_find_members_in_group + group, nested_group = bind_nested_groups(:member) + ldap.expect(:search, [nested_group], [base: group.dn, filter: posix_groups_filter]) + + md.expect(:find_group, group, ['foremaners', false]) + md.expect(:get_logins, ['katellers'], [nested_group[:member]]) + @posix.member_service = md + + assert_equal ['katellers'], @posix.users_for_gid('foremaners') end - def test_find_users_in_nested_groups - service_bind - group, nested_group = nested_groups + def test_find_users_in_netgroup + config.instance_variable_set(:@use_netgroups, true) + + group, nested_group = bind_nested_groups(:nisnetgrouptriple) + ldap.expect(:search, [nested_group], [base: group.dn, filter: group_class_filter('nisNetgroup')]) + + md.expect(:find_group, group, ['foremaners', false]) + md.expect(:get_netgroup_users, ['katellers'], [['testuser']]) + @posix.member_service = md + + assert_equal ['katellers'], @posix.users_for_gid('foremaners') + end - ldap.expect(:search, [nested_group], [base: group.dn, filter: group_class_filter]) - @posix.ldap = ldap + def test_users_in_non_exist_group + service_bind - basic_group(group) + md.expect(:find_group, nil) do |gid, only| + raise LdapFluff::Posix::MemberService::GIDNotFoundException if gid == 'foremaners' && only == false + end @posix.member_service = md + assert_equal [], @posix.users_for_gid('foremaners') + end + + def test_users_for_group + group, nested_group = bind_nested_groups(:member) + + ldap.expect(:search, [group], [filter: group_filter('foremaners'), base: config.group_base]) + ldap.expect(:search, [nested_group], [base: group.dn, filter: posix_groups_filter]) + @posix.member_service.ldap = ldap + assert_equal ['testuser'], @posix.users_for_gid('foremaners') end end From 0e20d70f377305ca9018de382f7777f06edeb264 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Sun, 17 Nov 2019 18:34:22 +0700 Subject: [PATCH 13/15] Make `Config` properties become changeable --- lib/ldap_fluff/ad_member_service.rb | 2 +- lib/ldap_fluff/config.rb | 35 ++++++++++++------------ lib/ldap_fluff/freeipa_member_service.rb | 2 +- lib/ldap_fluff/posix_member_service.rb | 2 +- test/config_test.rb | 2 +- test/ipa_test.rb | 2 +- test/ldap_test.rb | 2 +- test/ldap_test_helper.rb | 4 +-- test/posix_test.rb | 2 +- 9 files changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/ldap_fluff/ad_member_service.rb b/lib/ldap_fluff/ad_member_service.rb index d61c38c..6b2b306 100644 --- a/lib/ldap_fluff/ad_member_service.rb +++ b/lib/ldap_fluff/ad_member_service.rb @@ -5,7 +5,7 @@ class LdapFluff::ActiveDirectory::MemberService < LdapFluff::GenericMemberServic # @param [Net::LDAP] ldap # @param [LdapFluff::Config] config def initialize(ldap, config) - config.instance_variable_set(:@attr_login, 'samaccountname') unless config.attr_login + config.attr_login ||= 'samaccountname' super end diff --git a/lib/ldap_fluff/config.rb b/lib/ldap_fluff/config.rb index 401b369..6dfd6f8 100644 --- a/lib/ldap_fluff/config.rb +++ b/lib/ldap_fluff/config.rb @@ -2,9 +2,8 @@ class LdapFluff::Config ATTRIBUTES = [ - :host, :port, :encryption, :base_dn, :group_base, :server_type, :service_user, - :service_pass, :anon_queries, :attr_login, :search_filter, - :instrumentation_service, :use_netgroups + :host, :port, :encryption, :base_dn, :group_base, :server_type, :service_user, :service_pass, + :anon_queries, :attr_login, :search_filter, :instrumentation_service, :use_netgroups ].freeze DEFAULT_CONFIG = { @@ -14,37 +13,39 @@ class LdapFluff::Config group_base: 'dc=company,dc=com', server_type: :free_ipa, anon_queries: false, + attr_login: nil, + search_filter: nil, instrumentation_service: nil, use_netgroups: false }.freeze - # @!attribute [r] host + # @!attribute [rw] host # @return [String] - # @!attribute [r] port + # @!attribute [rw] port # @return [Integer] - # @!attribute [r] encryption + # @!attribute [rw] encryption # @return [Symbol, Hash] - # @!attribute [r] base_dn + # @!attribute [rw] base_dn # @return [String] - # @!attribute [r] group_base + # @!attribute [rw] group_base # @return [String] - # @!attribute [r] server_type + # @!attribute [rw] server_type # @return [Symbol] - # @!attribute [r] service_user + # @!attribute [rw] service_user # @return [String] - # @!attribute [r] service_pass + # @!attribute [rw] service_pass # @return [String] - # @!attribute [r] anon_queries + # @!attribute [rw] anon_queries # @return [Boolean] - # @!attribute [r] attr_login + # @!attribute [rw] attr_login # @return [String] - # @!attribute [r] search_filter + # @!attribute [rw] search_filter # @return [String] - # @!attribute [r] instrumentation_service + # @!attribute [rw] instrumentation_service # @return [#instrument] - # @!attribute [r] use_netgroups + # @!attribute [rw] use_netgroups # @return [Boolean] - attr_reader(*ATTRIBUTES) + attr_accessor(*ATTRIBUTES) # @param [#to_hash] config # @raise [ArgumentError] if config is not a Hash diff --git a/lib/ldap_fluff/freeipa_member_service.rb b/lib/ldap_fluff/freeipa_member_service.rb index eaa8e08..e22f2e8 100644 --- a/lib/ldap_fluff/freeipa_member_service.rb +++ b/lib/ldap_fluff/freeipa_member_service.rb @@ -4,7 +4,7 @@ class LdapFluff::FreeIPA::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap # @param [LdapFluff::Config] config def initialize(ldap, config) - config.instance_variable_set(:@attr_login, 'uid') unless config.attr_login + config.attr_login ||= 'uid' super end diff --git a/lib/ldap_fluff/posix_member_service.rb b/lib/ldap_fluff/posix_member_service.rb index 5637dee..6969134 100644 --- a/lib/ldap_fluff/posix_member_service.rb +++ b/lib/ldap_fluff/posix_member_service.rb @@ -5,7 +5,7 @@ class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap # @param [LdapFluff::Config] config def initialize(ldap, config) - config.instance_variable_set(:@attr_login, 'memberuid') unless config.attr_login + config.attr_login ||= 'memberuid' super end diff --git a/test/config_test.rb b/test/config_test.rb index aee3219..84c097a 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -31,7 +31,7 @@ def test_instrumentation_service end def test_missing_keys - assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new(CONFIG_HASH.reject { |_, v| v.nil? }) } + assert_raises(LdapFluff::Config::ConfigError) { LdapFluff.new } end def test_unknown_keys diff --git a/test/ipa_test.rb b/test/ipa_test.rb index 1bca2e0..43f6b6d 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -175,7 +175,7 @@ def test_insufficient_privileges_user end def test_find_users_for_netgroup - config.instance_variable_set(:@use_netgroups, true) + config.use_netgroups = true @ipa.member_service.ldap = service_bind group = Net::LDAP::Entry.new('gid=foremaners').tap { |g| g[:nisnetgrouptriple] = %w[(,john,) (,joe,)] } diff --git a/test/ldap_test.rb b/test/ldap_test.rb index e19f53c..9d19bd5 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -60,7 +60,7 @@ def test_invalid_user end def test_unknown_server_type - @fluff.ldap.config.instance_variable_set(:@server_type, nil) + @fluff.ldap.config.server_type = nil assert_raises(RuntimeError) { @fluff.send(:create_provider, @fluff.ldap.config) } end diff --git a/test/ldap_test_helper.rb b/test/ldap_test_helper.rb index 8d4bda0..cf1c0a7 100644 --- a/test/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -14,9 +14,7 @@ module LdapTestHelper group_base: 'ou=group,dc=internet,dc=com', service_user: 'service', service_pass: 'pass', - server_type: :free_ipa, - attr_login: nil, - search_filter: nil + server_type: :free_ipa }.freeze MOCK_VARS = %w[ldap md user_result].freeze diff --git a/test/posix_test.rb b/test/posix_test.rb index 13699e1..dffa8f8 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -153,7 +153,7 @@ def test_find_members_in_group end def test_find_users_in_netgroup - config.instance_variable_set(:@use_netgroups, true) + config.use_netgroups = true group, nested_group = bind_nested_groups(:nisnetgrouptriple) ldap.expect(:search, [nested_group], [base: group.dn, filter: group_class_filter('nisNetgroup')]) From 076d650b479cf7b95f17f34ef4ed026177c71d1e Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Tue, 19 Nov 2019 17:25:49 +0700 Subject: [PATCH 14/15] - Add `bind_dn_format` config to authenticate with LDAP using bind_dn correctly - Support AD binding with an email like `username@domain` - Fix circle-loop when biding with configured `service_user` - Do not lowercase the result login / group names --- ldap_fluff.gemspec | 2 +- lib/ldap_fluff/active_directory.rb | 18 ++++--------- lib/ldap_fluff/config.rb | 7 ++++-- lib/ldap_fluff/freeipa.rb | 28 ++++++--------------- lib/ldap_fluff/freeipa_member_service.rb | 6 ++--- lib/ldap_fluff/generic.rb | 22 +++++++++++++--- lib/ldap_fluff/generic_member_service.rb | 28 +++++++-------------- lib/ldap_fluff/posix.rb | 17 +++---------- test/ad_member_services_test.rb | 2 +- test/ad_test.rb | 8 ++++-- test/config_test.rb | 2 +- test/ipa_member_services_test.rb | 2 +- test/ipa_netgroup_member_services_test.rb | 2 +- test/ipa_test.rb | 21 ++++++++-------- test/ldap_test.rb | 2 +- test/posix_member_services_test.rb | 2 +- test/posix_netgroup_member_services_test.rb | 2 +- test/posix_test.rb | 6 ++++- 18 files changed, 81 insertions(+), 96 deletions(-) diff --git a/ldap_fluff.gemspec b/ldap_fluff.gemspec index 93e2b32..6bf5610 100644 --- a/ldap_fluff.gemspec +++ b/ldap_fluff.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'ldap_fluff' - s.version = '0.5.0' + s.version = '0.5.1' s.summary = 'LDAP querying tools for Active Directory, FreeIPA and POSIX-style' s.description = 'Simple library for binding & group querying on top of various LDAP implementations' s.homepage = 'https://github.com/theforeman/ldap_fluff' diff --git a/lib/ldap_fluff/active_directory.rb b/lib/ldap_fluff/active_directory.rb index bedac9e..1d894b6 100644 --- a/lib/ldap_fluff/active_directory.rb +++ b/lib/ldap_fluff/active_directory.rb @@ -1,19 +1,11 @@ # frozen_string_literal: true class LdapFluff::ActiveDirectory < LdapFluff::Generic - # @param [String] uid - # @param [String] password - # @param [Hash] opts - # @return [Boolean] - def bind?(uid = nil, password = nil, opts = {}) - if opts[:search] != false && uid && !(uid.include?(',') || uid.include?('\\')) - service_bind - user = member_service.find_user(uid, true) - - uid = user.dn if user - end - - super(uid, password) + # @param [LdapFluff::Config] config + def initialize(config) + config.bind_dn_format ||= "%s@#{config.base_dn.scan(/\bDC=([^,]*)/i).flatten.join('.')}" + super + @is_bind_dn = /(?, String] def get_users_for_member(member) - type = member.downcase.split(',')[1] - - if type == 'cn=users' + if member =~ /,(cn|ou)=users(,|$)/i member_service.get_logins([member]) - elsif type == 'cn=groups' - users_for_gid(member.split(',').first.split('=')[1]) + elsif member =~ /,(cn|ou)=groups(,|$)/i + users_for_gid(member.sub(/^.*?=([^,]*).*/, '\1')) end end end diff --git a/lib/ldap_fluff/freeipa_member_service.rb b/lib/ldap_fluff/freeipa_member_service.rb index e22f2e8..4e8e3b5 100644 --- a/lib/ldap_fluff/freeipa_member_service.rb +++ b/lib/ldap_fluff/freeipa_member_service.rb @@ -26,11 +26,11 @@ def find_user_groups(uid) # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com def get_groups(grouplist) grouplist.map do |g| - if g =~ /.*?\bipauniqueid=(.*?)/i - search = (ldap.search(base: g.downcase) || []).first + if g =~ /.*?\bipaUniqueID=/i + search = (ldap.search(base: g) || []).first search ? search[:cn].first : nil else - g.downcase.sub(/.*?\bcn=(.*?),.*/, '\1') + g.sub(/.*?\bCN=([^,]*).*/i, '\1') end end.compact end diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index 53cfb45..06da721 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -13,7 +13,8 @@ class LdapFluff::Generic # @param [LdapFluff::Config] config def initialize(config) - @config = config + @config = config + @is_bind_dn = /(?, Net::LDAP::Entry] # @raise [UIDNotFoundException] def find_user(uid, only = nil) - return find_by_dn(uid, only) if uid.include?(',') - # @type [Array] user = ldap.search(filter: name_filter(uid)) raise self.class::UIDNotFoundException if !user || user.empty? @@ -62,8 +61,6 @@ def find_by_dn(dn, only = nil) # @return [Array, Net::LDAP::Entry] # @raise [UIDNotFoundException] def find_group(gid, only = nil) - return find_by_dn(gid, only) if gid.include?(',') - # @type [Array] group = ldap.search(filter: group_filter(gid), base: config.group_base) raise self.class::GIDNotFoundException if !group || group.empty? @@ -87,8 +84,8 @@ def name_filter(uid, attr = nil) # @param [String] gid # @return [Net::LDAP::Filter] - def group_filter(gid) - Net::LDAP::Filter.eq('cn', gid) + def group_filter(gid, attr = 'cn') + Net::LDAP::Filter.eq(attr, gid) end # @param [String] name @@ -100,33 +97,26 @@ def class_filter(name) # extract the group names from the LDAP style response, # @param [Array] grouplist # @return [Array] will be something like CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com - def get_groups(grouplist) - grouplist.map { |g| g.downcase.sub(/.*?\bcn=(.*?),.*/, '\1') } + def get_groups(grouplist, attr = 'cn') + grouplist.map { |g| g.sub(/.*?\b#{attr}=([^,]*).*/i, '\1') } end # @param [Array] netgroup_triples # @return [Array] def get_netgroup_users(netgroup_triples) - return [] unless netgroup_triples - - netgroup_triples.map { |m| m.split(',')[1] } + netgroup_triples ? netgroup_triples.map { |m| m.split(',')[1] } : [] end # @param [Array] userlist # @return [Array] def get_logins(userlist) - userlist.map!(&:downcase) - - [config.attr_login, 'uid', 'cn'].map do |attribute| - logins = userlist.map { |g| g.sub(/.*?\b#{attribute}=(.*?),.*/, '\1') } - logins == userlist ? nil : logins - end.flatten.compact.uniq + userlist.map { |g| g.sub(/.*?\b(?:#{@login_attributes.join('|')})=([^,]*).*/i, '\1') } end # @param [Net::LDAP::Entry] entry # @return [String] def get_login_from_entry(entry) - [config.attr_login, 'uid', 'cn'].each do |attribute| + @login_attributes.each do |attribute| return entry.send(attribute) if entry.respond_to? attribute end diff --git a/lib/ldap_fluff/posix.rb b/lib/ldap_fluff/posix.rb index ff74893..b71291a 100644 --- a/lib/ldap_fluff/posix.rb +++ b/lib/ldap_fluff/posix.rb @@ -1,19 +1,10 @@ # frozen_string_literal: true class LdapFluff::Posix < LdapFluff::Generic - # @param [String] uid - # @param [String] password - # @param [Hash] opts - # @return [Boolean] - def bind?(uid = nil, password = nil, opts = {}) - if opts[:search] != false && uid && !uid.include?(',') - service_bind - user = member_service.find_user(uid, true) - - uid = user.dn if user - end - - super(uid, password) + # @param [LdapFluff::Config] config + def initialize(config) + config.bind_dn_format ||= "uid=%s,ou=users,#{config.base_dn}" + super end private diff --git a/test/ad_member_services_test.rb b/test/ad_member_services_test.rb index 056ffd7..b9df258 100644 --- a/test/ad_member_services_test.rb +++ b/test/ad_member_services_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestADMemberService < MiniTest::Test include LdapTestHelper diff --git a/test/ad_test.rb b/test/ad_test.rb index 098521a..e6919d6 100644 --- a/test/ad_test.rb +++ b/test/ad_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestAD < MiniTest::Test include LdapTestHelper @@ -10,6 +10,10 @@ def setup @ad = LdapFluff::ActiveDirectory.new(config) end + def service_bind(user = nil, pass = nil, ret = true) + super(user || "service@#{CONFIG_HASH[:host]}", pass || 'pass', ret) + end + def test_good_bind # no expectation on the service account service_bind('EXAMPLE\\internet', 'password') @@ -61,7 +65,7 @@ def test_bad_user end def test_bad_service_user - service_bind('service', 'pass', false) + service_bind(nil, nil, false) assert_raises(LdapFluff::ActiveDirectory::UnauthenticatedException) do @ad.groups_for_uid('john') diff --git a/test/config_test.rb b/test/config_test.rb index 84c097a..a90260f 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class ConfigTest < MiniTest::Test include LdapTestHelper diff --git a/test/ipa_member_services_test.rb b/test/ipa_member_services_test.rb index 3875b7e..18fb9ec 100644 --- a/test/ipa_member_services_test.rb +++ b/test/ipa_member_services_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestIPAMemberService < MiniTest::Test include LdapTestHelper diff --git a/test/ipa_netgroup_member_services_test.rb b/test/ipa_netgroup_member_services_test.rb index 6318e36..0fd76e1 100644 --- a/test/ipa_netgroup_member_services_test.rb +++ b/test/ipa_netgroup_member_services_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestIPANetgroupMemberService < MiniTest::Test include LdapTestHelper diff --git a/test/ipa_test.rb b/test/ipa_test.rb index 43f6b6d..b022d3a 100644 --- a/test/ipa_test.rb +++ b/test/ipa_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestIPA < MiniTest::Test include LdapTestHelper @@ -10,34 +10,33 @@ def setup @ipa = LdapFluff::FreeIPA.new(config) end - # default setup for service bind users - def service_bind(user = 'service', pass = 'pass', ret = true) - super(ipa_user_bind(user), pass, ret) + def service_bind(user = nil, pass = nil, ret = true) + super(user || ipa_user_bind('service'), pass || 'pass', ret) end # looks up the uid's full DN via the service account def test_good_bind - user_result.expect(:dn, ipa_user_bind('internet')) + user_result.expect(:dn, uid = ipa_user_bind('internet')) md.expect(:find_user, user_result, ['internet', true]) @ipa.member_service = md service_bind - service_bind('internet', 'password') + service_bind(uid.dup, 'password') assert @ipa.bind?('internet', 'password') end def test_good_bind_with_dn # no expectation on the service account - service_bind('internet', 'password') + service_bind(uid = ipa_user_bind('internet'), 'password') - assert @ipa.bind?(ipa_user_bind('internet'), 'password') + assert @ipa.bind?(uid.dup, 'password') end def test_bad_bind - service_bind('internet', 'password', false) + service_bind(uid = ipa_user_bind('internet'), 'password', false) - refute @ipa.bind?(ipa_user_bind('internet'), 'password') + refute @ipa.bind?(uid.dup, 'password') end def test_groups @@ -59,7 +58,7 @@ def test_bad_user end def test_bad_service_user - service_bind('service', 'pass', false) + service_bind(nil, nil, false) assert_raises(LdapFluff::FreeIPA::UnauthenticatedException) do @ipa.groups_for_uid('john') diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 9d19bd5..d341133 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestLDAP < MiniTest::Test include LdapTestHelper diff --git a/test/posix_member_services_test.rb b/test/posix_member_services_test.rb index 3df6ba8..70021fa 100644 --- a/test/posix_member_services_test.rb +++ b/test/posix_member_services_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestPosixMemberService < MiniTest::Test include LdapTestHelper diff --git a/test/posix_netgroup_member_services_test.rb b/test/posix_netgroup_member_services_test.rb index 7d67e10..e4dd6c9 100644 --- a/test/posix_netgroup_member_services_test.rb +++ b/test/posix_netgroup_member_services_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestPosixNetgroupMemberService < MiniTest::Test include LdapTestHelper diff --git a/test/posix_test.rb b/test/posix_test.rb index dffa8f8..6189c72 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'ldap_test_helper' +require_relative 'ldap_test_helper' class TestPosix < MiniTest::Test include LdapTestHelper @@ -10,6 +10,10 @@ def setup @posix = LdapFluff::Posix.new(config) end + def service_bind(user = nil, pass = nil, ret = true) + super(user || "uid=service,ou=users,#{CONFIG_HASH[:base_dn]}", pass || 'pass', ret) + end + def test_groups service_bind basic_user From 8eb7ac348259a17b3fba91cf674a8ab73dbcfe66 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Tue, 19 Nov 2019 18:27:24 +0700 Subject: [PATCH 15/15] - Remove deprecated methods (unused anywhere) - Add `attr_member` config to correct Posix LDAP groups finding --- lib/ldap_fluff/config.rb | 8 ++++++-- lib/ldap_fluff/generic.rb | 10 ---------- lib/ldap_fluff/generic_member_service.rb | 4 ++-- lib/ldap_fluff/posix_member_service.rb | 17 +++-------------- test/ldap_test.rb | 14 -------------- test/ldap_test_helper.rb | 6 +++--- test/posix_member_services_test.rb | 13 ++----------- test/posix_test.rb | 2 +- 8 files changed, 17 insertions(+), 57 deletions(-) diff --git a/lib/ldap_fluff/config.rb b/lib/ldap_fluff/config.rb index 1c31aab..44cb09a 100644 --- a/lib/ldap_fluff/config.rb +++ b/lib/ldap_fluff/config.rb @@ -3,7 +3,8 @@ class LdapFluff::Config ATTRIBUTES = [ :host, :port, :encryption, :base_dn, :group_base, :server_type, :service_user, :service_pass, - :anon_queries, :attr_login, :search_filter, :instrumentation_service, :use_netgroups, :bind_dn_format + :anon_queries, :attr_login, :search_filter, :instrumentation_service, :use_netgroups, + :bind_dn_format, :attr_member ].freeze DEFAULT_CONFIG = { @@ -17,7 +18,8 @@ class LdapFluff::Config search_filter: nil, instrumentation_service: nil, use_netgroups: false, - bind_dn_format: nil + bind_dn_format: nil, + attr_member: nil }.freeze # @!attribute [rw] host @@ -48,6 +50,8 @@ class LdapFluff::Config # @return [Boolean] # @!attribute [rw] bind_dn_format # @return [String] + # @!attribute [rw] attr_member + # @return [String] attr_accessor(*ATTRIBUTES) # @param [#to_hash] config diff --git a/lib/ldap_fluff/generic.rb b/lib/ldap_fluff/generic.rb index 06da721..a5b80c2 100644 --- a/lib/ldap_fluff/generic.rb +++ b/lib/ldap_fluff/generic.rb @@ -82,16 +82,6 @@ def user_in_groups?(uid, gids = [], all = true) all ? (intersection.sort == gids.sort) : !intersection.empty? end - # @param [String] cn - # @return [Boolean] - # @deprecated - def includes_cn?(cn) - service_bind - search = ldap.search(filter: Net::LDAP::Filter.eq('cn', cn)) - # NOTE: present? - !(search.respond_to?(:empty?) ? search.empty? : !search) - end - # @raise [UnauthenticatedException] def service_bind return if config.anon_queries || bind?(config.service_user, config.service_pass, search: false) diff --git a/lib/ldap_fluff/generic_member_service.rb b/lib/ldap_fluff/generic_member_service.rb index 1da632e..f486abe 100644 --- a/lib/ldap_fluff/generic_member_service.rb +++ b/lib/ldap_fluff/generic_member_service.rb @@ -77,8 +77,8 @@ def find_user_groups(uid) # @param [String] uid # @return [Net::LDAP::Filter] - def name_filter(uid, attr = nil) - filter = Net::LDAP::Filter.eq(attr || config.attr_login, uid) + def name_filter(uid, attr = config.attr_login) + filter = Net::LDAP::Filter.eq(attr, uid) search_filter ? (filter & search_filter) : filter end diff --git a/lib/ldap_fluff/posix_member_service.rb b/lib/ldap_fluff/posix_member_service.rb index 6969134..6256ce4 100644 --- a/lib/ldap_fluff/posix_member_service.rb +++ b/lib/ldap_fluff/posix_member_service.rb @@ -5,7 +5,8 @@ class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService # @param [Net::LDAP] ldap # @param [LdapFluff::Config] config def initialize(ldap, config) - config.attr_login ||= 'memberuid' + config.attr_login ||= 'uid' + config.attr_member ||= 'memberuid' super end @@ -31,24 +32,12 @@ def find_user(uid, only = nil, base_dn = nil) # @return [Array] an LDAP user with groups attached # @note this method is not particularly fast for large LDAP systems def find_user_groups(uid) - groups = ldap.search(filter: name_filter(uid), base: config.group_base) + groups = ldap.search(filter: group_filter(uid, config.attr_member), base: config.group_base) return [] unless groups groups.map { |entry| entry[:cn].first } end - # @param [String] uid - # @param [Array] gids - # @deprecated - def times_in_groups(uid, gids, all = false) - filters = gids.map { |cn| group_filter(cn) } - # AND or OR all of the filters together - group_filters = filters.reduce(all ? :& : :|) - filter = name_filter(uid) & group_filters - - (ldap.search(base: config.group_base, filter: filter) || []).size - end - class UIDNotFoundException < LdapFluff::Error end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index d341133..68c69a3 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -99,18 +99,4 @@ def test_found_group assert_equal group, @fluff.find_group('bros') end - - # @deprecated - def test_includes_cn - service_bind ipa_user_bind('service') - ldap.expect(:search, [nil], [filter: group_filter('bros')]) - - assert @fluff.ldap.send(:includes_cn?, 'bros') - end - - private - - def test_instance_variable - @fluff.ldap - end end diff --git a/test/ldap_test_helper.rb b/test/ldap_test_helper.rb index cf1c0a7..b0dbd8e 100644 --- a/test/ldap_test_helper.rb +++ b/test/ldap_test_helper.rb @@ -65,8 +65,8 @@ def bigtime_user basic_user(%w[bros broskies]) end - def group_filter(cn) - Net::LDAP::Filter.eq('cn', cn) + def group_filter(cn, attr = 'cn') + Net::LDAP::Filter.eq(attr, cn) end def group_class_filter(name = 'group') @@ -79,7 +79,7 @@ def group_class_filter(name = 'group') # @return [Net::LDAP::Filter] # @!method ipa_name_filter # @return [Net::LDAP::Filter] - { ad: 'samaccountname', posix: 'memberuid', ipa: 'uid' }.each do |key, attr| + { ad: 'samaccountname', posix: 'uid', ipa: 'uid' }.each do |key, attr| define_method("#{key}_name_filter".to_sym) { |name| Net::LDAP::Filter.eq(attr, name) } end diff --git a/test/posix_member_services_test.rb b/test/posix_member_services_test.rb index 70021fa..25d0eee 100644 --- a/test/posix_member_services_test.rb +++ b/test/posix_member_services_test.rb @@ -21,14 +21,14 @@ def test_find_user def test_find_user_groups user = posix_group_payload - ldap.expect(:search, user, [filter: posix_name_filter('john'), base: config.group_base]) + ldap.expect(:search, user, [filter: group_filter('john', 'memberuid'), base: config.group_base]) @ms.ldap = ldap assert_equal ['broze'], @ms.find_user_groups('john') end def test_find_no_groups - ldap.expect(:search, [], [filter: posix_name_filter('john'), base: config.group_base]) + ldap.expect(:search, [], [filter: group_filter('john', 'memberuid'), base: config.group_base]) @ms.ldap = ldap assert_equal [], @ms.find_user_groups('john') @@ -63,13 +63,4 @@ def test_group_doesnt_exists assert_raises(LdapFluff::Posix::MemberService::GIDNotFoundException) { @ms.find_group('broze') } end - - # @deprecated - def test_times_in_groups - filter = posix_name_filter('uid') & (group_filter('gid') | group_filter('s')) - ldap.expect(:search, nil, [base: config.group_base, filter: filter]) - @ms.ldap = ldap - - assert_equal 0, @ms.send(:times_in_groups, 'uid', %w[gid s]) - end end diff --git a/test/posix_test.rb b/test/posix_test.rb index 6189c72..5098d51 100644 --- a/test/posix_test.rb +++ b/test/posix_test.rb @@ -130,7 +130,7 @@ def bind_nested_groups(attr = :memberuid) group[attr] = ['katellers'] nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com') - nested_group[attr] = [attr == :member ? 'memberuid=testuser,' : 'testuser'] + nested_group[attr] = [attr == :member ? 'uid=testuser,' : 'testuser'] [group, nested_group] end