From ee14d5b8955cb94b71de26a53965318b1cc124b3 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 17:37:52 -0700 Subject: [PATCH 001/307] Add classic, recursive membership validators --- lib/github/ldap.rb | 1 + lib/github/ldap/membership_validators.rb | 17 +++++ lib/github/ldap/membership_validators/base.rb | 24 +++++++ .../ldap/membership_validators/classic.rb | 26 ++++++++ .../ldap/membership_validators/recursive.rb | 63 +++++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 lib/github/ldap/membership_validators.rb create mode 100644 lib/github/ldap/membership_validators/base.rb create mode 100644 lib/github/ldap/membership_validators/classic.rb create mode 100644 lib/github/ldap/membership_validators/recursive.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 9844ceb..1484bec 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -9,6 +9,7 @@ class Ldap require 'github/ldap/virtual_group' require 'github/ldap/virtual_attributes' require 'github/ldap/instrumentation' + require 'github/ldap/membership_validators' include Instrumentation diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb new file mode 100644 index 0000000..dc3bd30 --- /dev/null +++ b/lib/github/ldap/membership_validators.rb @@ -0,0 +1,17 @@ +module GitHub + class Ldap + # x + # + # For example: + # + # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, %w(Engineering)) + # validator.perform(entry) #=> true + # + module MembershipValidators + require 'github/ldap/membership_validators/base' + + autoload :Classic, 'github/ldap/membership_validators/classic' + autoload :Recursive, 'github/ldap/membership_validators/recursive' + end + end +end diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb new file mode 100644 index 0000000..e9ecee5 --- /dev/null +++ b/lib/github/ldap/membership_validators/base.rb @@ -0,0 +1,24 @@ +module GitHub + module Ldap + module MembershipValidators + class Base + attr_reader :ldap, :groups + + def initialize(ldap, groups) + @ldap = ldap + @groups = groups + end + + # Abstract: Performs the membership validation check. + # + # Returns Boolean whether the entry's membership is validated or not. + # def perform(entry) + # end + + def domains + ldap.search_domains + end + end + end + end +end diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb new file mode 100644 index 0000000..f1a626e --- /dev/null +++ b/lib/github/ldap/membership_validators/classic.rb @@ -0,0 +1,26 @@ +module GitHub + module Ldap + module MembershipValidators + # Validates membership using the `Domain#membership` lookup method. + # + # This is a simple wrapper for existing functionality in order to expose + # it consistently with the new approach. + class Classic < Base + def perform(entry) + return true if groups.empty? + + domains.each do |base_name, domain| + membership = domain.membership(entry, groups) + + if !membership.empty? + entry[:groups] = membership + return true + end + end + + false + end + end + end + end +end diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb new file mode 100644 index 0000000..204dbfe --- /dev/null +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -0,0 +1,63 @@ +module GitHub + module Ldap + module MembershipValidators + # Validates membership recursively. + # + # The first step checks whether the entry is a direct member of the given + # groups. If they are, then we've validated membership successfully. + # + # If not, query for all of the groups that have our groups as members, + # then we check if the entry is a member of any of those. + # + # This is repeated until the entry is found, recursing and requesting + # groups in bulk each iteration until we hit the maximum depth allowed + # and have to give up. + # + # This results in a maximum of `depth` queries (per domain) to validate + # membership in a list of groups. + class Recursive < Base + include Filter + + DEFAULT_MAX_DEPTH = 9 + + def perform(entry, depth = DEFAULT_MAX_DEPTH) + domains.each do |base_name, domain| + # find groups entry is an immediate member of + membership = domain.search(filter: member_filter(entry), attributes: %w(dn)).map(&:dn) + # success if any of these groups match the restricted auth groups + return true if membership.any?{ |dn| groups.include?(dn) } + + # give up if the entry has no memberships to recurse + next if membership.empty? + + # recurse to at most `depth` + depth.times do |n| + # find groups whose members include membership groups + membership = domain.search(filter: membership_filter(membership), attributes: %w(dn)).map(&:dn) + # success if any of these groups match the restricted auth groups + return true if membership.any?{ |dn| groups.include?(dn) } + + # give up if there are no more membersips to recurse + break if membership.empty? + end + + # give up on this base if there are no memberships to test + next if membership.empty? + end + + false + end + + # Internal: Construct a filter to find groups whose members are the + # Array of String group DNs passed in. + # + # FIXME: Not portable (hardcoded to `member` attribute). + # + # Returns a String filter. + def membership_filter(groups) + "(|%s)" % groups.map{ |dn| "(member=#{dn})" }.join + end + end + end + end +end From 72a543ab59fa9422aad3054169a3f559d1ea2a1c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 17:56:04 -0700 Subject: [PATCH 002/307] GitHub::Ldap is a class How could I forget. --- lib/github/ldap/membership_validators/classic.rb | 2 +- lib/github/ldap/membership_validators/recursive.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb index f1a626e..b032fe9 100644 --- a/lib/github/ldap/membership_validators/classic.rb +++ b/lib/github/ldap/membership_validators/classic.rb @@ -1,5 +1,5 @@ module GitHub - module Ldap + class Ldap module MembershipValidators # Validates membership using the `Domain#membership` lookup method. # diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 204dbfe..95f9528 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -1,5 +1,5 @@ module GitHub - module Ldap + class Ldap module MembershipValidators # Validates membership recursively. # From c2dcbba3e325f97f082316c60d715f1906e7706d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 17:57:22 -0700 Subject: [PATCH 003/307] Fix doco for MembershipValidators module --- lib/github/ldap/membership_validators.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index dc3bd30..89d1ff9 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -1,6 +1,6 @@ module GitHub class Ldap - # x + # Provides various strategies for validating membership. # # For example: # From f412a055b241f67af815446ab2d89818699ea19f Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 18:23:31 -0700 Subject: [PATCH 004/307] Missed Base --- lib/github/ldap/membership_validators/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb index e9ecee5..eda4b94 100644 --- a/lib/github/ldap/membership_validators/base.rb +++ b/lib/github/ldap/membership_validators/base.rb @@ -1,5 +1,5 @@ module GitHub - module Ldap + class Ldap module MembershipValidators class Base attr_reader :ldap, :groups From 4017f92f1925080334b75c4c9a7b515091b1709a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 19:07:44 -0700 Subject: [PATCH 005/307] Significant fixes and :nail_care: --- lib/github/ldap/membership_validators/base.rb | 2 +- lib/github/ldap/membership_validators/classic.rb | 13 +++++++++++-- lib/github/ldap/membership_validators/recursive.rb | 13 ++++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb index eda4b94..96b77b0 100644 --- a/lib/github/ldap/membership_validators/base.rb +++ b/lib/github/ldap/membership_validators/base.rb @@ -16,7 +16,7 @@ def initialize(ldap, groups) # end def domains - ldap.search_domains + @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } end end end diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb index b032fe9..b59a018 100644 --- a/lib/github/ldap/membership_validators/classic.rb +++ b/lib/github/ldap/membership_validators/classic.rb @@ -9,8 +9,8 @@ class Classic < Base def perform(entry) return true if groups.empty? - domains.each do |base_name, domain| - membership = domain.membership(entry, groups) + domains.each do |domain| + membership = domain.membership(entry, group_names) if !membership.empty? entry[:groups] = membership @@ -20,6 +20,15 @@ def perform(entry) false end + + # Internal: the group names to look up membership for. + # + # FIXME: Hardcoded to CN. + # + # Returns an Array of String group names (CNs). + def group_names + @group_names ||= groups.map { |g| g[:cn].first } + end end end end diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 95f9528..0f2596c 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -21,11 +21,11 @@ class Recursive < Base DEFAULT_MAX_DEPTH = 9 def perform(entry, depth = DEFAULT_MAX_DEPTH) - domains.each do |base_name, domain| + domains.each do |domain| # find groups entry is an immediate member of membership = domain.search(filter: member_filter(entry), attributes: %w(dn)).map(&:dn) # success if any of these groups match the restricted auth groups - return true if membership.any?{ |dn| groups.include?(dn) } + return true if membership.any?{ |dn| group_dns.include?(dn) } # give up if the entry has no memberships to recurse next if membership.empty? @@ -35,7 +35,7 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # find groups whose members include membership groups membership = domain.search(filter: membership_filter(membership), attributes: %w(dn)).map(&:dn) # success if any of these groups match the restricted auth groups - return true if membership.any?{ |dn| groups.include?(dn) } + return true if membership.any?{ |dn| group_dns.include?(dn) } # give up if there are no more membersips to recurse break if membership.empty? @@ -57,6 +57,13 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) def membership_filter(groups) "(|%s)" % groups.map{ |dn| "(member=#{dn})" }.join end + + # Internal: the group DNs to check against. + # + # Returns an Array of String DNs. + def group_dns + @group_dns ||= groups.map(&:dn) + end end end end From 4dbf604b46b692d3b0345d3a45c3db471fa53d07 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 19:08:28 -0700 Subject: [PATCH 006/307] Add rudimentary tests for MembershipValidators --- test/membership_validators_test.rb | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/membership_validators_test.rb diff --git a/test/membership_validators_test.rb b/test/membership_validators_test.rb new file mode 100644 index 0000000..f33bee9 --- /dev/null +++ b/test/membership_validators_test.rb @@ -0,0 +1,54 @@ +require_relative 'test_helper' + +module GitHubLdapMembershipValidatorsTestCases + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(Enterprise)) + assert validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_group + validator = make_validator(%w(People)) + refute validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_any_group + @entry = @domain.user?('ldaptest') + validator = make_validator(%w(Enterprise People)) + refute validator.perform(@entry) + end +end + +class GitHubLdapMembershipValidatorsClassicTest < GitHub::Ldap::Test + include GitHubLdapMembershipValidatorsTestCases + + def self.test_server_options + {search_domains: "dc=github,dc=com"} + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('calavera') + @validator = GitHub::Ldap::MembershipValidators::Classic + end +end + +class GitHubLdapMembershipValidatorsRecursiveTest < GitHub::Ldap::Test + include GitHubLdapMembershipValidatorsTestCases + + def self.test_server_options + {search_domains: "dc=github,dc=com"} + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('calavera') + @validator = GitHub::Ldap::MembershipValidators::Recursive + end +end From 00e8a036918a89bfb83d1f3364c71d46ff6168f0 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 19:32:46 -0700 Subject: [PATCH 007/307] Doco base methods --- lib/github/ldap/membership_validators/base.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb index 96b77b0..a625555 100644 --- a/lib/github/ldap/membership_validators/base.rb +++ b/lib/github/ldap/membership_validators/base.rb @@ -4,6 +4,10 @@ module MembershipValidators class Base attr_reader :ldap, :groups + # Public: Instantiate new validator. + # + # - ldap: GitHub::Ldap object + # - groups: Array of Net::LDAP::Entry group objects def initialize(ldap, groups) @ldap = ldap @groups = groups @@ -15,6 +19,9 @@ def initialize(ldap, groups) # def perform(entry) # end + # Internal: Domains to search through. + # + # Returns an Array of GitHub::Ldap::Domain objects. def domains @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } end From 8d87584e4074d904e19c67b1350f86f8603ee341 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 20:06:18 -0700 Subject: [PATCH 008/307] CNs are hardcoded elsewhere --- lib/github/ldap/membership_validators/classic.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb index b59a018..e46bbae 100644 --- a/lib/github/ldap/membership_validators/classic.rb +++ b/lib/github/ldap/membership_validators/classic.rb @@ -23,8 +23,6 @@ def perform(entry) # Internal: the group names to look up membership for. # - # FIXME: Hardcoded to CN. - # # Returns an Array of String group names (CNs). def group_names @group_names ||= groups.map { |g| g[:cn].first } From 65dbcb9fea28c8d724ec5d6cabaf9fadb4989936 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 20:28:21 -0700 Subject: [PATCH 009/307] Accept String DN for member_filter --- lib/github/ldap/filter.rb | 8 +++++--- test/filter_test.rb | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/filter.rb b/lib/github/ldap/filter.rb index a238642..c25bef2 100644 --- a/lib/github/ldap/filter.rb +++ b/lib/github/ldap/filter.rb @@ -20,16 +20,18 @@ def group_filter(group_names) # Filter to check group membership. # - # entry: finds groups this Net::LDAP::Entry is a member of (optional) + # entry: finds groups this entry is a member of (optional) + # Expects a Net::LDAP::Entry or String DN. # # Returns a Net::LDAP::Filter. def member_filter(entry = nil) if entry + entry = entry.dn if entry.respond_to?(:dn) MEMBERSHIP_NAMES. - map {|n| Net::LDAP::Filter.eq(n, entry.dn) }.reduce(:|) + map {|n| Net::LDAP::Filter.eq(n, entry) }.reduce(:|) else MEMBERSHIP_NAMES. - map {|n| Net::LDAP::Filter.pres(n) }. reduce(:|) + map {|n| Net::LDAP::Filter.pres(n) }. reduce(:|) end end diff --git a/test/filter_test.rb b/test/filter_test.rb index 8fc6ba2..47a45e4 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -32,6 +32,11 @@ def test_member_equal @subject.member_filter(@entry).to_s end + def test_member_equal_with_string + assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))", + @subject.member_filter(@entry.dn).to_s + end + def test_posix_member_without_uid @entry.uid = nil assert_nil @subject.posix_member_filter(@entry, @ldap.uid) From 97ba49b0000b3fbfcc789ed39bd2567480e2be09 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 20:34:06 -0700 Subject: [PATCH 010/307] Use member_filter for Recursive group search --- lib/github/ldap/membership_validators/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 0f2596c..1e7cdf3 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -55,7 +55,7 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # # Returns a String filter. def membership_filter(groups) - "(|%s)" % groups.map{ |dn| "(member=#{dn})" }.join + groups.map{ |dn| member_filter(dn) }.reduce(:|) end # Internal: the group DNs to check against. From 90fb34ad598059243e55914ac118f4853dc7043a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 20:34:27 -0700 Subject: [PATCH 011/307] Remove FIXME --- lib/github/ldap/membership_validators/recursive.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 1e7cdf3..3ed2ed6 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -51,8 +51,6 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # Internal: Construct a filter to find groups whose members are the # Array of String group DNs passed in. # - # FIXME: Not portable (hardcoded to `member` attribute). - # # Returns a String filter. def membership_filter(groups) groups.map{ |dn| member_filter(dn) }.reduce(:|) From a1d9d791ccfbc4bb478df37670e844592b3c9d5e Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 23 Sep 2014 20:56:18 -0700 Subject: [PATCH 012/307] Extract attributes --- lib/github/ldap/membership_validators/recursive.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 3ed2ed6..6405960 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -19,11 +19,13 @@ class Recursive < Base include Filter DEFAULT_MAX_DEPTH = 9 + ATTRS = %w(dn) def perform(entry, depth = DEFAULT_MAX_DEPTH) domains.each do |domain| # find groups entry is an immediate member of - membership = domain.search(filter: member_filter(entry), attributes: %w(dn)).map(&:dn) + membership = domain.search(filter: member_filter(entry), attributes: ATTRS).map(&:dn) + # success if any of these groups match the restricted auth groups return true if membership.any?{ |dn| group_dns.include?(dn) } @@ -33,7 +35,8 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # recurse to at most `depth` depth.times do |n| # find groups whose members include membership groups - membership = domain.search(filter: membership_filter(membership), attributes: %w(dn)).map(&:dn) + membership = domain.search(filter: membership_filter(membership), attributes: ATTRS).map(&:dn) + # success if any of these groups match the restricted auth groups return true if membership.any?{ |dn| group_dns.include?(dn) } From 7f8bf868901a681d98b7b51fc7bbd144f2ba7116 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 25 Sep 2014 18:05:39 -0700 Subject: [PATCH 013/307] Fix subgroup fixture CNs Didn't match DN so resulted in false positives. --- test/fixtures/github-with-subgroups.ldif | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/fixtures/github-with-subgroups.ldif b/test/fixtures/github-with-subgroups.ldif index 00dc929..c432edc 100644 --- a/test/fixtures/github-with-subgroups.ldif +++ b/test/fixtures/github-with-subgroups.ldif @@ -50,19 +50,19 @@ member: uid=user1,ou=users,dc=github,dc=com member: cn=group1.1,ou=groups,dc=github,dc=com dn: cn=group1.1,ou=groups,dc=github,dc=com -cn: group1 +cn: group1.1 objectClass: groupOfNames member: uid=user1.1,ou=users,dc=github,dc=com member: cn=group1.1.1,ou=groups,dc=github,dc=com dn: cn=group1.1.1,ou=groups,dc=github,dc=com -cn: group1 +cn: group1.1.1 objectClass: groupOfNames member: uid=user1.1.1,ou=users,dc=github,dc=com member: cn=group1.1.1.1,ou=groups,dc=github,dc=com dn: cn=group1.1.1.1,ou=groups,dc=github,dc=com -cn: group1 +cn: group1.1.1.1 objectClass: groupOfNames member: uid=user1.1.1.1,ou=users,dc=github,dc=com From 8bb15865f2b069a5668df2f98e0c67181bc70793 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 25 Sep 2014 18:06:32 -0700 Subject: [PATCH 014/307] Add recursion tests for classic, recursive strategies --- test/membership_validators/classic_test.rb | 52 ++++++++++++++++++ test/membership_validators/recursive_test.rb | 57 ++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 test/membership_validators/classic_test.rb create mode 100644 test/membership_validators/recursive_test.rb diff --git a/test/membership_validators/classic_test.rb b/test/membership_validators/classic_test.rb new file mode 100644 index 0000000..1db60ba --- /dev/null +++ b/test/membership_validators/classic_test.rb @@ -0,0 +1,52 @@ +require_relative '../test_helper' + +class GitHubLdapClassicMembershipValidatorsTest < GitHub::Ldap::Test + def self.test_server_options + { search_domains: "dc=github,dc=com", + user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s + } + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1.1.1.1') + @validator = GitHub::Ldap::MembershipValidators::Classic + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(group1.1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_child_group + validator = make_validator(%w(group1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_grandchild_group + validator = make_validator(%w(group1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_great_grandchild_group + validator = make_validator(%w(group1)) + assert validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_group + validator = make_validator(%w(Enterprise)) + refute validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_any_group + @entry = @domain.user?('admin') + validator = make_validator(%w(Enterprise)) + refute validator.perform(@entry) + end +end diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb new file mode 100644 index 0000000..183cbf0 --- /dev/null +++ b/test/membership_validators/recursive_test.rb @@ -0,0 +1,57 @@ +require_relative '../test_helper' + +class GitHubLdapRecursiveMembershipValidatorsTest < GitHub::Ldap::Test + def self.test_server_options + { search_domains: "dc=github,dc=com", + user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s + } + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1.1.1.1') + @validator = GitHub::Ldap::MembershipValidators::Recursive + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(group1.1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_child_group + validator = make_validator(%w(group1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_grandchild_group + validator = make_validator(%w(group1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_great_grandchild_group + validator = make_validator(%w(group1)) + assert validator.perform(@entry) + end + + def test_does_not_validate_user_in_great_granchild_group_with_depth + validator = make_validator(%w(group1)) + refute validator.perform(@entry, 2) + end + + def test_does_not_validate_user_not_in_group + validator = make_validator(%w(Enterprise)) + refute validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_any_group + @entry = @domain.user?('admin') + validator = make_validator(%w(Enterprise)) + refute validator.perform(@entry) + end +end From e97af8d4af9107205e28d4159365c1d583def625 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 14:32:21 -0700 Subject: [PATCH 015/307] Autoload MembershipValidators::Base too h/t @jch --- lib/github/ldap/membership_validators.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index 89d1ff9..6a3deef 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -8,8 +8,7 @@ class Ldap # validator.perform(entry) #=> true # module MembershipValidators - require 'github/ldap/membership_validators/base' - + autoload :Base, 'github/ldap/membership_validators/base' autoload :Classic, 'github/ldap/membership_validators/classic' autoload :Recursive, 'github/ldap/membership_validators/recursive' end From 26d6bfd1b04f580f94df55ee5866a4aa414681a0 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 14:32:47 -0700 Subject: [PATCH 016/307] Tweak docs --- lib/github/ldap/membership_validators.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index 6a3deef..9e3b49d 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -4,7 +4,8 @@ class Ldap # # For example: # - # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, %w(Engineering)) + # groups = domain.groups(%w(Engineering)) + # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, groups) # validator.perform(entry) #=> true # module MembershipValidators From 3efbdc15f37259e3fcd98ae2441b82043f078dbc Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 14:36:00 -0700 Subject: [PATCH 017/307] Document attrs, make internal method private --- lib/github/ldap/membership_validators/base.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb index a625555..3c47853 100644 --- a/lib/github/ldap/membership_validators/base.rb +++ b/lib/github/ldap/membership_validators/base.rb @@ -2,7 +2,12 @@ module GitHub class Ldap module MembershipValidators class Base - attr_reader :ldap, :groups + + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Internal: an Array of Net::LDAP::Entry group objects to validate with. + attr_reader :groups # Public: Instantiate new validator. # @@ -25,6 +30,7 @@ def initialize(ldap, groups) def domains @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } end + private :domains end end end From 0b98dc5cbedf69467053444de1c0470dbf183ca5 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 14:37:16 -0700 Subject: [PATCH 018/307] Refer to full class name in docs --- lib/github/ldap/membership_validators/classic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb index e46bbae..f1cda3e 100644 --- a/lib/github/ldap/membership_validators/classic.rb +++ b/lib/github/ldap/membership_validators/classic.rb @@ -1,7 +1,7 @@ module GitHub class Ldap module MembershipValidators - # Validates membership using the `Domain#membership` lookup method. + # Validates membership using `GitHub::Ldap::Domain#membership`. # # This is a simple wrapper for existing functionality in order to expose # it consistently with the new approach. From 76cefb895c8b1b8739bdf48979056d48e791aa22 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 14:38:47 -0700 Subject: [PATCH 019/307] Give these blocks some breathing room --- lib/github/ldap/membership_validators/recursive.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 6405960..65df2f3 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -27,7 +27,7 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) membership = domain.search(filter: member_filter(entry), attributes: ATTRS).map(&:dn) # success if any of these groups match the restricted auth groups - return true if membership.any?{ |dn| group_dns.include?(dn) } + return true if membership.any? { |dn| group_dns.include?(dn) } # give up if the entry has no memberships to recurse next if membership.empty? @@ -38,7 +38,7 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) membership = domain.search(filter: membership_filter(membership), attributes: ATTRS).map(&:dn) # success if any of these groups match the restricted auth groups - return true if membership.any?{ |dn| group_dns.include?(dn) } + return true if membership.any? { |dn| group_dns.include?(dn) } # give up if there are no more membersips to recurse break if membership.empty? @@ -56,7 +56,7 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # # Returns a String filter. def membership_filter(groups) - groups.map{ |dn| member_filter(dn) }.reduce(:|) + groups.map { |dn| member_filter(dn) }.reduce(:|) end # Internal: the group DNs to check against. From d31baf56898b2771be3b97f8a26d97b5a8cc6ed8 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 15:35:24 -0700 Subject: [PATCH 020/307] Accept UID String for posix_member_filter --- lib/github/ldap/filter.rb | 14 ++++++++++---- test/filter_test.rb | 8 +++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/filter.rb b/lib/github/ldap/filter.rb index c25bef2..64f5aa3 100644 --- a/lib/github/ldap/filter.rb +++ b/lib/github/ldap/filter.rb @@ -43,10 +43,16 @@ def member_filter(entry = nil) # uid_attr: specifies the memberUid attribute to match with # # Returns a Net::LDAP::Filter or nil if no entry has no UID set. - def posix_member_filter(entry, uid_attr) - if !entry[uid_attr].empty? - entry[uid_attr].map { |uid| Net::LDAP::Filter.eq("memberUid", uid) }. - reduce(:|) + def posix_member_filter(entry_or_uid, uid_attr = nil) + case entry_or_uid + when Net::LDAP::Entry + entry = entry_or_uid + if !entry[uid_attr].empty? + entry[uid_attr].map { |uid| Net::LDAP::Filter.eq("memberUid", uid) }. + reduce(:|) + end + when String + Net::LDAP::Filter.eq("memberUid", entry_or_uid) end end diff --git a/test/filter_test.rb b/test/filter_test.rb index 47a45e4..58992a8 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -20,7 +20,8 @@ def setup @subject = Subject.new(@ldap) @me = 'uid=calavera,dc=github,dc=com' @uid = "calavera" - @entry = Entry.new(@me, @uid) + @entry = Net::LDAP::Entry.new(@me) + @entry[:uid] = @uid end def test_member_present @@ -47,6 +48,11 @@ def test_posix_member_equal @subject.posix_member_filter(@entry, @ldap.uid).to_s end + def test_posix_member_equal_string + assert_equal "(memberUid=#{@uid})", + @subject.posix_member_filter(@uid).to_s + end + def test_groups_reduced assert_equal "(|(cn=Enterprise)(cn=People))", @subject.group_filter(%w(Enterprise People)).to_s From 45bf499a6335909f09f0ba797a8d186efd820bee Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 16:52:45 -0700 Subject: [PATCH 021/307] Handle posixGroup memberships --- .../ldap/membership_validators/recursive.rb | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 65df2f3..7d4936c 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -19,15 +19,15 @@ class Recursive < Base include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(dn) + ATTRS = %w(dn cn) def perform(entry, depth = DEFAULT_MAX_DEPTH) domains.each do |domain| # find groups entry is an immediate member of - membership = domain.search(filter: member_filter(entry), attributes: ATTRS).map(&:dn) + membership = domain.search(filter: member_filter(entry), attributes: ATTRS) # success if any of these groups match the restricted auth groups - return true if membership.any? { |dn| group_dns.include?(dn) } + return true if membership.any? { |entry| group_dns.include?(entry.dn) } # give up if the entry has no memberships to recurse next if membership.empty? @@ -35,10 +35,10 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) # recurse to at most `depth` depth.times do |n| # find groups whose members include membership groups - membership = domain.search(filter: membership_filter(membership), attributes: ATTRS).map(&:dn) + membership = domain.search(filter: membership_filter(membership), attributes: ATTRS) # success if any of these groups match the restricted auth groups - return true if membership.any? { |dn| group_dns.include?(dn) } + return true if membership.any? { |entry| group_dns.include?(entry.dn) } # give up if there are no more membersips to recurse break if membership.empty? @@ -51,12 +51,31 @@ def perform(entry, depth = DEFAULT_MAX_DEPTH) false end + # Internal: Construct a filter to find groups this entry is a direct + # member of. + # + # Overloads the included `GitHub::Ldap::Filters#member_filter` method + # to inject `posixGroup` handling. + # + # Returns a Net::LDAP::Filter object. + def member_filter(entry_or_uid, uid = ldap.uid) + filter = super(entry_or_uid) + + if ldap.posix_support_enabled? + if posix_filter = posix_member_filter(entry_or_uid, uid) + filter |= posix_filter + end + end + + filter + end + # Internal: Construct a filter to find groups whose members are the # Array of String group DNs passed in. # # Returns a String filter. def membership_filter(groups) - groups.map { |dn| member_filter(dn) }.reduce(:|) + groups.map { |entry| member_filter(entry, :cn) }.reduce(:|) end # Internal: the group DNs to check against. From 5d7d6f426b68b3a3bb1c5cc3c0cac17ea3e34d23 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 16:53:06 -0700 Subject: [PATCH 022/307] Add deep nested posixGroup membership fixtures --- test/fixtures/github-with-posixGroups.ldif | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/fixtures/github-with-posixGroups.ldif b/test/fixtures/github-with-posixGroups.ldif index ac8b3a0..5269784 100644 --- a/test/fixtures/github-with-posixGroups.ldif +++ b/test/fixtures/github-with-posixGroups.ldif @@ -27,6 +27,29 @@ objectClass: posixGroup memberUid: benburkert memberUid: mtodd +dn: cn=group1,ou=groups,dc=github,dc=com +cn: group1 +objectClass: posixGroup +memberUid: group1.1 +memberUid: user1 + +dn: cn=group1.1,ou=groups,dc=github,dc=com +cn: group1.1 +objectClass: posixGroup +memberUid: group1.1.1 +memberUid: user1.1 + +dn: cn=group1.1.1,ou=groups,dc=github,dc=com +cn: group1.1.1 +objectClass: posixGroup +memberUid: group1.1.1.1 +memberUid: user1.1.1 + +dn: cn=group1.1.1.1,ou=groups,dc=github,dc=com +cn: group1.1.1.1 +objectClass: posixGroup +memberUid: user1.1.1.1 + # Users dn: ou=users,dc=github,dc=com @@ -48,3 +71,35 @@ uid: mtodd userPassword: passworD1 mail: mtodd@github.com objectClass: inetOrgPerson + +dn: uid=user1,ou=users,dc=github,dc=com +cn: user1 +sn: user1 +uid: user1 +userPassword: passworD1 +mail: user1@github.com +objectClass: inetOrgPerson + +dn: uid=user1.1,ou=users,dc=github,dc=com +cn: user1.1 +sn: user1.1 +uid: user1.1 +userPassword: passworD1 +mail: user1.1@github.com +objectClass: inetOrgPerson + +dn: uid=user1.1.1,ou=users,dc=github,dc=com +cn: user1.1.1 +sn: user1.1.1 +uid: user1.1.1 +userPassword: passworD1 +mail: user1.1.1@github.com +objectClass: inetOrgPerson + +dn: uid=user1.1.1.1,ou=users,dc=github,dc=com +cn: user1.1.1.1 +sn: user1.1.1.1 +uid: user1.1.1.1 +userPassword: passworD1 +mail: user1.1.1.1@github.com +objectClass: inetOrgPerson From 084cdf23986d0b7b359fbc490d4724fd9f8c2639 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 16:55:22 -0700 Subject: [PATCH 023/307] Add tests for recursive posixGroup membership validation --- test/membership_validators/classic_test.rb | 27 +++++++++++ test/membership_validators/recursive_test.rb | 48 ++++++++++++++++++++ test/membership_validators_test.rb | 8 +++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/test/membership_validators/classic_test.rb b/test/membership_validators/classic_test.rb index 1db60ba..7802826 100644 --- a/test/membership_validators/classic_test.rb +++ b/test/membership_validators/classic_test.rb @@ -50,3 +50,30 @@ def test_does_not_validate_user_not_in_any_group refute validator.perform(@entry) end end + +class GitHubLdapClassicMembershipValidatorsWithPosixGroupsTest < GitHub::Ldap::Test + def self.test_server_options + { search_domains: "dc=github,dc=com", + uid: "uid", + custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), + user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s + } + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1.1.1.1') + @validator = GitHub::Ldap::MembershipValidators::Classic + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(group1.1.1.1)) + assert validator.perform(@entry) + end +end diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb index 183cbf0..fd8e7ee 100644 --- a/test/membership_validators/recursive_test.rb +++ b/test/membership_validators/recursive_test.rb @@ -3,6 +3,7 @@ class GitHubLdapRecursiveMembershipValidatorsTest < GitHub::Ldap::Test def self.test_server_options { search_domains: "dc=github,dc=com", + uid: "uid", user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s } end @@ -55,3 +56,50 @@ def test_does_not_validate_user_not_in_any_group refute validator.perform(@entry) end end + +class GitHubLdapRecursiveMembershipValidatorsWithPosixGroupsTest < GitHub::Ldap::Test + def self.test_server_options + { search_domains: "dc=github,dc=com", + uid: "uid", + custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), + user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s + } + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1.1.1.1') + @validator = GitHub::Ldap::MembershipValidators::Recursive + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(group1.1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_child_group + validator = make_validator(%w(group1.1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_grandchild_group + validator = make_validator(%w(group1.1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_great_grandchild_group + validator = make_validator(%w(group1)) + assert validator.perform(@entry) + end + + def test_does_not_validate_user_in_great_granchild_group_with_depth + validator = make_validator(%w(group1)) + refute validator.perform(@entry, 2) + end +end diff --git a/test/membership_validators_test.rb b/test/membership_validators_test.rb index f33bee9..77f0358 100644 --- a/test/membership_validators_test.rb +++ b/test/membership_validators_test.rb @@ -27,7 +27,9 @@ class GitHubLdapMembershipValidatorsClassicTest < GitHub::Ldap::Test include GitHubLdapMembershipValidatorsTestCases def self.test_server_options - {search_domains: "dc=github,dc=com"} + { search_domains: "dc=github,dc=com", + uid: "uid" + } end def setup @@ -42,7 +44,9 @@ class GitHubLdapMembershipValidatorsRecursiveTest < GitHub::Ldap::Test include GitHubLdapMembershipValidatorsTestCases def self.test_server_options - {search_domains: "dc=github,dc=com"} + { search_domains: "dc=github,dc=com", + uid: "uid" + } end def setup From 58a5315f2b1b2f3d0f742dfd3793a562acae5711 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 17:09:52 -0700 Subject: [PATCH 024/307] Hook up separate OpenLDAP install, build steps --- .travis.yml | 12 ++++++++++++ script/cibuild-apacheds | 3 +++ script/cibuild-openldap | 3 +++ script/install-openldap | 3 +++ 4 files changed, 21 insertions(+) create mode 100755 script/cibuild-apacheds create mode 100755 script/cibuild-openldap create mode 100755 script/install-openldap diff --git a/.travis.yml b/.travis.yml index 8f17123..8536179 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,17 @@ rvm: - 1.9.3 - 2.1.0 +env: + matrix: + - TESTENV=openldap + - TESTENV=apacheds + +install: + - if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi + +script: + - if [ "$TESTENV" = "openldap" ]; then ./script/cibuild-openldap; fi + - if [ "$TESTENV" = "apacheds" ]; then ./script/cibuild-apacheds; fi + notifications: email: false diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds new file mode 100755 index 0000000..8fdaec9 --- /dev/null +++ b/script/cibuild-apacheds @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +rake diff --git a/script/cibuild-openldap b/script/cibuild-openldap new file mode 100755 index 0000000..0304526 --- /dev/null +++ b/script/cibuild-openldap @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +echo "OpenLDAP test runner stub" diff --git a/script/install-openldap b/script/install-openldap new file mode 100755 index 0000000..815cb9a --- /dev/null +++ b/script/install-openldap @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +echo "OpenLDAP installer stub" From 87592057c84e999c9731d75fb3990d894d78c574 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 17:17:26 -0700 Subject: [PATCH 025/307] RVM exec rake for testing ApacheDS tests --- script/cibuild-apacheds | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds index 8fdaec9..b5f928f 100755 --- a/script/cibuild-apacheds +++ b/script/cibuild-apacheds @@ -1,3 +1,3 @@ #!/usr/bin/env sh -rake +rvm exec rake From 5257be052c2d87f2c933c895fae73f57a2a62cbd Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 17:19:53 -0700 Subject: [PATCH 026/307] Fix build badge [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66f7073..eb86a8b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Build Status](https://travis-ci.org/github/github-ldap.png) +![Build Status](https://travis-ci.org/github/github-ldap.png?branch=master) # Github::Ldap From 392410f220a8d89eaa445a327cb9e4af06385470 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 26 Sep 2014 17:23:03 -0700 Subject: [PATCH 027/307] Simplify a bit --- script/cibuild-apacheds | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds index b5f928f..dee4823 100755 --- a/script/cibuild-apacheds +++ b/script/cibuild-apacheds @@ -1,3 +1,4 @@ #!/usr/bin/env sh -rvm exec rake +cd .. +rake From 5844fe617662cdb410eacb1e93f116744be29c59 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 16:31:58 -0700 Subject: [PATCH 028/307] Fast finish, simplify script, remove extraneous bits --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8536179..c567448 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,19 @@ language: ruby rvm: - - 1.9.3 - - 2.1.0 + - 1.9.3 + - 2.1.0 env: - matrix: - - TESTENV=openldap - - TESTENV=apacheds + - TESTENV=openldap + - TESTENV=apacheds install: - if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi script: - - if [ "$TESTENV" = "openldap" ]; then ./script/cibuild-openldap; fi - - if [ "$TESTENV" = "apacheds" ]; then ./script/cibuild-apacheds; fi + - ./script/cibuild-$TESTENV +matrix: + fast_finish: true notifications: email: false From 2397389cb0d2efd415c56bc728cb28caf22abc3e Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 16:32:31 -0700 Subject: [PATCH 029/307] Compute relative path to cd into, add debugging output --- script/cibuild-apacheds | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds index dee4823..0da077d 100755 --- a/script/cibuild-apacheds +++ b/script/cibuild-apacheds @@ -1,4 +1,9 @@ #!/usr/bin/env sh +set -e +set -x + +echo pwd +cd `dirname $0`/.. +echo pwd -cd .. rake From efcd72ed2fc0299a63f49becbc5b805556d342f3 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 16:48:51 -0700 Subject: [PATCH 030/307] Bundle exec --- script/cibuild-apacheds | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds index 0da077d..7244dfe 100755 --- a/script/cibuild-apacheds +++ b/script/cibuild-apacheds @@ -2,8 +2,8 @@ set -e set -x -echo pwd +echo `pwd` cd `dirname $0`/.. -echo pwd +echo `pwd` -rake +bundle exec rake From f566ababd730b9fd28c9dac87f65b8209ae57ebf Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 16:52:49 -0700 Subject: [PATCH 031/307] bundle install --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c567448..09e4709 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ env: install: - if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi + - bundle install script: - ./script/cibuild-$TESTENV From 4efafa69cb76824d7d091d7848a702a2285eb9ee Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 17:33:18 -0700 Subject: [PATCH 032/307] Install, configure, seed OpenLDAP --- script/install-openldap | 29 +++ test/fixtures/openldap/memberof.ldif | 33 +++ test/fixtures/openldap/seed.ldif | 316 +++++++++++++++++++++++++ test/fixtures/openldap/slapd.conf.ldif | 67 ++++++ 4 files changed, 445 insertions(+) create mode 100644 test/fixtures/openldap/memberof.ldif create mode 100644 test/fixtures/openldap/seed.ldif create mode 100644 test/fixtures/openldap/slapd.conf.ldif diff --git a/script/install-openldap b/script/install-openldap index 815cb9a..3ef514d 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -1,3 +1,32 @@ #!/usr/bin/env sh echo "OpenLDAP installer stub" + +DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils + +BASE_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../test/fixtures/openldap && pwd )" + +# setup base configuration, schemas +sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif +# load memberof and ref-int overlays and configure them +sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif + +# Add base domain. +sudo slapadd -F /etc/ldap/slapd.d < Date: Mon, 29 Sep 2014 17:39:14 -0700 Subject: [PATCH 033/307] :fire: unnecessary debugging --- script/cibuild-apacheds | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/cibuild-apacheds b/script/cibuild-apacheds index 7244dfe..6e02fa0 100755 --- a/script/cibuild-apacheds +++ b/script/cibuild-apacheds @@ -2,8 +2,6 @@ set -e set -x -echo `pwd` cd `dirname $0`/.. -echo `pwd` bundle exec rake From af39c0ce4afe3cc2d1d1b59de8b004b727f0b7ee Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 17:39:40 -0700 Subject: [PATCH 034/307] Fail on error, adjust base path computation --- script/install-openldap | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/script/install-openldap b/script/install-openldap index 3ef514d..6f47260 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -1,10 +1,14 @@ #!/usr/bin/env sh +set -e +set -x echo "OpenLDAP installer stub" DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils -BASE_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../test/fixtures/openldap && pwd )" +BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )" + +sudo /etc/init.d/slapd stop # setup base configuration, schemas sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif From 6e2d2c30583890d44ed148cf31666bc3ee74f5e1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 21:05:23 -0700 Subject: [PATCH 035/307] Wipe out old config first --- script/install-openldap | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/script/install-openldap b/script/install-openldap index 6f47260..22921b0 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -2,17 +2,24 @@ set -e set -x -echo "OpenLDAP installer stub" - -DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils +TMPDIR=$(mktemp -d) +cd $TMPDIR BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )" +DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils + sudo /etc/init.d/slapd stop -# setup base configuration, schemas +ls -lah /etc/ldap/slapd.d + +# Delete data and reconfigure. +sudo cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG +sudo rm -rf /etc/ldap/slapd.d/* +sudo rm -rf /var/lib/ldap/* +sudo cp -v ./DB_CONFIG /var/lib/ldap/DB_CONFIG sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif -# load memberof and ref-int overlays and configure them +# Load memberof and ref-int overlays and configure them. sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif # Add base domain. @@ -34,3 +41,5 @@ sudo /etc/init.d/slapd start cat $BASE_PATH/seed.ldif | /usr/bin/time sudo ldapadd -x -D "cn=admin,dc=github,dc=org" -w passworD1 \ -h localhost -p 389 + +sudo rm -rf $TMPDIR From e4f0a198b2acb3da1b1eefe138dac468c48c95cc Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 21:09:16 -0700 Subject: [PATCH 036/307] Switch things around to keep the path sane --- script/install-openldap | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/script/install-openldap b/script/install-openldap index 22921b0..c6c67c7 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -2,16 +2,14 @@ set -e set -x -TMPDIR=$(mktemp -d) -cd $TMPDIR - BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )" DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils sudo /etc/init.d/slapd stop -ls -lah /etc/ldap/slapd.d +TMPDIR=$(mktemp -d) +cd $TMPDIR # Delete data and reconfigure. sudo cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG From 2e4ce0fcc1b681e8964d42d6f3d8590f1c06ebd2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 21:27:45 -0700 Subject: [PATCH 037/307] Run the test suite with OpenLDAP --- script/cibuild-openldap | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/script/cibuild-openldap b/script/cibuild-openldap index 0304526..6e02fa0 100755 --- a/script/cibuild-openldap +++ b/script/cibuild-openldap @@ -1,3 +1,7 @@ #!/usr/bin/env sh +set -e +set -x -echo "OpenLDAP test runner stub" +cd `dirname $0`/.. + +bundle exec rake From 19d2fb9ab2f347456f8705872e17c3d9ea51e34b Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 21:37:53 -0700 Subject: [PATCH 038/307] Go back to dc=github,dc=com to match existing fixtures --- script/install-openldap | 4 +- test/fixtures/openldap/seed.ldif | 240 ++++++++++++------------- test/fixtures/openldap/slapd.conf.ldif | 8 +- 3 files changed, 126 insertions(+), 126 deletions(-) diff --git a/script/install-openldap b/script/install-openldap index c6c67c7..bdbeae9 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -22,7 +22,7 @@ sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif # Add base domain. sudo slapadd -F /etc/ldap/slapd.d < Date: Mon, 29 Sep 2014 21:40:05 -0700 Subject: [PATCH 039/307] Start test server with ladle when in apacheds test env only --- test/test_helper.rb | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index d996c5f..720d988 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,6 +13,10 @@ require 'minitest/autorun' class GitHub::Ldap::Test < Minitest::Test + def self.test_env + ENV['TESTENV'] || "apacheds" + end + def self.run(reporter, options = {}) start_server super @@ -20,26 +24,46 @@ def self.run(reporter, options = {}) end def self.stop_server - GitHub::Ldap.stop_server + if test_env == "apacheds" + GitHub::Ldap.stop_server + end end def self.start_server - server_opts = respond_to?(:test_server_options) ? test_server_options : {} - GitHub::Ldap.start_server(server_opts) + if test_env == "apacheds" + server_opts = respond_to?(:test_server_options) ? test_server_options : {} + GitHub::Ldap.start_server(server_opts) + end end def options @service = MockInstrumentationService.new - @options ||= GitHub::Ldap.server_options.merge \ - host: 'localhost', - uid: 'uid', - :instrumentation_service => @service + @options ||= + case self.class.test_env + when "apacheds" + GitHub::Ldap.server_options.merge \ + host: 'localhost', + uid: 'uid', + instrumentation_service: @service + when "openldap" + { + host: 'localhost', + port: 389 + admin_user: 'uid=admin,dc=github,dc=com', + admin_password: 'passworD1', + search_domains: %w(dc=github,dc=com), + uid: 'uid', + instrumentation_service: @service + } + end end end class GitHub::Ldap::UnauthenticatedTest < GitHub::Ldap::Test def self.start_server - GitHub::Ldap.start_server(:allow_anonymous => true) + if test_env == "apacheds" + GitHub::Ldap.start_server(:allow_anonymous => true) + end end def options From 33ad5a893291fd57b7daf8eb09758e6d6ee88286 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 29 Sep 2014 21:54:11 -0700 Subject: [PATCH 040/307] I hear commas are good --- test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 720d988..f12ddbc 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -48,7 +48,7 @@ def options when "openldap" { host: 'localhost', - port: 389 + port: 389, admin_user: 'uid=admin,dc=github,dc=com', admin_password: 'passworD1', search_domains: %w(dc=github,dc=com), From 94d5b828c8de14e12f1f6c776840d36cb9822ec6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 5 Oct 2014 18:56:22 -0700 Subject: [PATCH 041/307] Create a 'common' fixture file --- test/fixtures/common/seed.ldif | 336 +++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 test/fixtures/common/seed.ldif diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif new file mode 100644 index 0000000..3067949 --- /dev/null +++ b/test/fixtures/common/seed.ldif @@ -0,0 +1,336 @@ +dn: ou=People,dc=github,dc=com +objectClass: top +objectClass: organizationalUnit +ou: People + +dn: ou=Groups,dc=github,dc=com +objectClass: top +objectClass: organizationalUnit +ou: Groups + +# Directory Superuser +dn: uid=admin,dc=github,dc=com +uid: admin +cn: system administrator +sn: administrator +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +displayName: Directory Superuser +userPassword: passworD1 + +# Users 1-10 + +dn: uid=user1,ou=People,dc=github,dc=com +uid: user1 +cn: user1 +sn: user1 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user1@openldap.ghe.local + +dn: uid=user2,ou=People,dc=github,dc=com +uid: user2 +cn: user2 +sn: user2 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user2@openldap.ghe.local + +dn: uid=user3,ou=People,dc=github,dc=com +uid: user3 +cn: user3 +sn: user3 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user3@openldap.ghe.local + +dn: uid=user4,ou=People,dc=github,dc=com +uid: user4 +cn: user4 +sn: user4 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user4@openldap.ghe.local + +dn: uid=user5,ou=People,dc=github,dc=com +uid: user5 +cn: user5 +sn: user5 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user5@openldap.ghe.local + +dn: uid=user6,ou=People,dc=github,dc=com +uid: user6 +cn: user6 +sn: user6 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user6@openldap.ghe.local + +dn: uid=user7,ou=People,dc=github,dc=com +uid: user7 +cn: user7 +sn: user7 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user7@openldap.ghe.local + +dn: uid=user8,ou=People,dc=github,dc=com +uid: user8 +cn: user8 +sn: user8 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user8@openldap.ghe.local + +dn: uid=user9,ou=People,dc=github,dc=com +uid: user9 +cn: user9 +sn: user9 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user9@openldap.ghe.local + +dn: uid=user10,ou=People,dc=github,dc=com +uid: user10 +cn: user10 +sn: user10 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: user10@openldap.ghe.local + +# Emailless User 1 + +dn: uid=emailless-user1,ou=People,dc=github,dc=com +uid: emailless-user1 +cn: emailless-user1 +sn: emailless-user1 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 + +# Admin User 1 + +dn: uid=admin1,ou=People,dc=github,dc=com +uid: admin1 +cn: admin1 +sn: admin1 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 +mail: admin1@openldap.ghe.local + +# Groups + +dn: cn=ghe-users,ou=Groups,dc=github,dc=com +cn: ghe-users +objectClass: groupOfNames +member: uid=user1,ou=People,dc=github,dc=com +member: uid=emailless-user1,ou=People,dc=github,dc=com + +dn: cn=all-users,ou=Groups,dc=github,dc=com +cn: all-users +objectClass: groupOfNames +member: cn=ghe-users,ou=Groups,dc=github,dc=com +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=user3,ou=People,dc=github,dc=com +member: uid=user4,ou=People,dc=github,dc=com +member: uid=user5,ou=People,dc=github,dc=com +member: uid=user6,ou=People,dc=github,dc=com +member: uid=user7,ou=People,dc=github,dc=com +member: uid=user8,ou=People,dc=github,dc=com +member: uid=user9,ou=People,dc=github,dc=com +member: uid=user10,ou=People,dc=github,dc=com +member: uid=emailless-user1,ou=People,dc=github,dc=com + +dn: cn=ghe-admins,ou=Groups,dc=github,dc=com +cn: ghe-admins +objectClass: groupOfNames +member: uid=admin1,ou=People,dc=github,dc=com + +dn: cn=all-admins,ou=Groups,dc=github,dc=com +cn: all-admins +objectClass: groupOfNames +member: cn=ghe-admins,ou=Groups,dc=github,dc=com +member: uid=admin1,ou=People,dc=github,dc=com + +dn: cn=n-member-group10,ou=Groups,dc=github,dc=com +cn: n-member-group10 +objectClass: groupOfNames +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=user3,ou=People,dc=github,dc=com +member: uid=user4,ou=People,dc=github,dc=com +member: uid=user5,ou=People,dc=github,dc=com +member: uid=user6,ou=People,dc=github,dc=com +member: uid=user7,ou=People,dc=github,dc=com +member: uid=user8,ou=People,dc=github,dc=com +member: uid=user9,ou=People,dc=github,dc=com +member: uid=user10,ou=People,dc=github,dc=com + +dn: cn=nested-group1,ou=Groups,dc=github,dc=com +cn: nested-group1 +objectClass: groupOfNames +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=user3,ou=People,dc=github,dc=com +member: uid=user4,ou=People,dc=github,dc=com +member: uid=user5,ou=People,dc=github,dc=com +member: uid=user6,ou=People,dc=github,dc=com +member: uid=user7,ou=People,dc=github,dc=com +member: uid=user8,ou=People,dc=github,dc=com +member: uid=user9,ou=People,dc=github,dc=com +member: uid=user10,ou=People,dc=github,dc=com + +dn: cn=nested-groups,ou=Groups,dc=github,dc=com +cn: nested-groups +objectClass: groupOfNames +member: cn=nested-group1,ou=Groups,dc=github,dc=com + +dn: cn=n-member-nested-group1,ou=Groups,dc=github,dc=com +cn: n-member-nested-group1 +objectClass: groupOfNames +member: cn=nested-group1,ou=Groups,dc=github,dc=com + +dn: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com +cn: deeply-nested-group0.0.0 +objectClass: groupOfNames +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=user3,ou=People,dc=github,dc=com +member: uid=user4,ou=People,dc=github,dc=com +member: uid=user5,ou=People,dc=github,dc=com + +dn: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com +cn: deeply-nested-group0.0.1 +objectClass: groupOfNames +member: uid=user6,ou=People,dc=github,dc=com +member: uid=user7,ou=People,dc=github,dc=com +member: uid=user8,ou=People,dc=github,dc=com +member: uid=user9,ou=People,dc=github,dc=com +member: uid=user10,ou=People,dc=github,dc=com + +dn: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com +cn: deeply-nested-group0.0 +objectClass: groupOfNames +member: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com +member: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com + +dn: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com +cn: deeply-nested-group0 +objectClass: groupOfNames +member: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com + +dn: cn=deeply-nested-groups,ou=Groups,dc=github,dc=com +cn: deeply-nested-groups +objectClass: groupOfNames +member: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group1 +objectClass: groupOfNames +member: cn=nested-group1,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group2 +objectClass: groupOfNames +member: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group3 +objectClass: groupOfNames +member: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group4 +objectClass: groupOfNames +member: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group5 +objectClass: groupOfNames +member: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group6 +objectClass: groupOfNames +member: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group7 +objectClass: groupOfNames +member: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group8 +objectClass: groupOfNames +member: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com + +dn: cn=n-depth-nested-group9,ou=Groups,dc=github,dc=com +cn: n-depth-nested-group9 +objectClass: groupOfNames +member: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com + +dn: cn=head-group,ou=Groups,dc=github,dc=com +cn: head-group +objectClass: groupOfNames +member: cn=tail-group,ou=Groups,dc=github,dc=com +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=user3,ou=People,dc=github,dc=com +member: uid=user4,ou=People,dc=github,dc=com +member: uid=user5,ou=People,dc=github,dc=com + +dn: cn=tail-group,ou=Groups,dc=github,dc=com +cn: tail-group +objectClass: groupOfNames +member: cn=head-group,ou=Groups,dc=github,dc=com +member: uid=user6,ou=People,dc=github,dc=com +member: uid=user7,ou=People,dc=github,dc=com +member: uid=user8,ou=People,dc=github,dc=com +member: uid=user9,ou=People,dc=github,dc=com +member: uid=user10,ou=People,dc=github,dc=com + +dn: cn=recursively-nested-groups,ou=Groups,dc=github,dc=com +cn: recursively-nested-groups +objectClass: groupOfNames +member: cn=head-group,ou=Groups,dc=github,dc=com +member: cn=tail-group,ou=Groups,dc=github,dc=com From d2065f7ae6d3741daa3e93dc6db82af42ce8c7d9 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 5 Oct 2014 18:58:30 -0700 Subject: [PATCH 042/307] Hardcode server options This is to reduce the variations each test creates, so all tests can pass with the same settings, fixtures. --- test/test_helper.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index f12ddbc..3408a72 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -29,10 +29,18 @@ def self.stop_server end end + def self.test_server_options + { + custom_schemas: FIXTURES.join('posixGroup.schema.ldif').to_s, + user_fixtures: FIXTURES.join('common/seed.ldif').to_s, + allow_anonymous: true, + verbose: ENV.fetch("VERBOSE", "0") == "1" + } + end + def self.start_server if test_env == "apacheds" - server_opts = respond_to?(:test_server_options) ? test_server_options : {} - GitHub::Ldap.start_server(server_opts) + GitHub::Ldap.start_server(test_server_options) end end @@ -42,6 +50,8 @@ def options case self.class.test_env when "apacheds" GitHub::Ldap.server_options.merge \ + admin_user: 'uid=admin,dc=github,dc=com', + admin_password: 'passworD1', host: 'localhost', uid: 'uid', instrumentation_service: @service @@ -60,12 +70,6 @@ def options end class GitHub::Ldap::UnauthenticatedTest < GitHub::Ldap::Test - def self.start_server - if test_env == "apacheds" - GitHub::Ldap.start_server(:allow_anonymous => true) - end - end - def options @options ||= begin super.delete_if {|k, _| [:admin_user, :admin_password].include?(k)} From 1e2b52ee73354568a560f099a737df75e49c03c3 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 11:01:28 -0700 Subject: [PATCH 043/307] Remove test-specific fixtures, use common seed Overrides local adaptor settings, though. --- test/domain_test.rb | 46 +++++++++------------------------------- test/group_test.rb | 12 ----------- test/posix_group_test.rb | 4 ---- 3 files changed, 10 insertions(+), 52 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 470e00d..6c0d1e4 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -143,10 +143,6 @@ class GitHubLdapDomainUnauthenticatedTest < GitHub::Ldap::UnauthenticatedTest end class GitHubLdapDomainNestedGroupsTest < GitHub::Ldap::Test - def self.test_server_options - {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s} - end - def setup @ldap = GitHub::Ldap.new(options) @domain = @ldap.domain("dc=github,dc=com") @@ -168,17 +164,10 @@ def test_membership_in_deeply_nested_subgroups end class GitHubLdapPosixGroupsWithRecursionFallbackTest < GitHub::Ldap::Test - def self.test_server_options - { - custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), - user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s, - # so we exercise the recursive group search fallback - recursive_group_search_fallback: true - } - end - def setup - @ldap = GitHub::Ldap.new(options) + opts = options.merge \ + recursive_group_search_fallback: true + @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") @cn = "enterprise-posix-devs" end @@ -192,17 +181,10 @@ def test_membership_for_posixGroups end class GitHubLdapPosixGroupsWithoutRecursionTest < GitHub::Ldap::Test - def self.test_server_options - { - custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), - user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s, - # so we test the test the non-recursive group membership search - recursive_group_search_fallback: false - } - end - def setup - @ldap = GitHub::Ldap.new(options) + opts = options.merge \ + recursive_group_search_fallback: false + @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") @cn = "enterprise-posix-devs" end @@ -218,19 +200,11 @@ def test_membership_for_posixGroups # Specifically testing that this doesn't break when posixGroups are not # supported. class GitHubLdapWithoutPosixGroupsTest < GitHub::Ldap::Test - def self.test_server_options - { - custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), - user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s, - # so we test the test the non-recursive group membership search - recursive_group_search_fallback: false, - # explicitly disable posixGroup support (even if the schema supports it) - posix_support: false - } - end - def setup - @ldap = GitHub::Ldap.new(options) + opts = options.merge \ + recursive_group_search_fallback: false, # test non-recursive group membership search + posix_support: false # disable posixGroup support + @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") @cn = "enterprise-posix-devs" end diff --git a/test/group_test.rb b/test/group_test.rb index 6f1714d..2bde806 100644 --- a/test/group_test.rb +++ b/test/group_test.rb @@ -1,10 +1,6 @@ require_relative 'test_helper' class GitHubLdapGroupTest < GitHub::Ldap::Test - def self.test_server_options - {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s} - end - def groups_domain @ldap.domain("ou=groups,dc=github,dc=com") end @@ -62,10 +58,6 @@ def test_unknown_group end class GitHubLdapLoopedGroupTest < GitHub::Ldap::Test - def self.test_server_options - {user_fixtures: FIXTURES.join('github-with-looped-subgroups.ldif').to_s} - end - def setup @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com") end @@ -76,10 +68,6 @@ def test_members_from_subgroups end class GitHubLdapMissingEntriesTest < GitHub::Ldap::Test - def self.test_server_options - {user_fixtures: FIXTURES.join('github-with-missing-entries.ldif').to_s} - end - def setup @ldap = GitHub::Ldap.new(options) end diff --git a/test/posix_group_test.rb b/test/posix_group_test.rb index a71e252..4f9d815 100644 --- a/test/posix_group_test.rb +++ b/test/posix_group_test.rb @@ -1,10 +1,6 @@ require_relative 'test_helper' class GitHubLdapPosixGroupTest < GitHub::Ldap::Test - def self.test_server_options - {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s} - end - def setup @simple_group = Net::LDAP::Entry._load(""" dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com From f0a3d7579abd1547abccaf41c6afcfaf6553a191 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 11:01:59 -0700 Subject: [PATCH 044/307] Remove quiet option when verbose is set Helpful for debugging since quiet will override verbose in practice. --- lib/github/ldap/server.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/github/ldap/server.rb b/lib/github/ldap/server.rb index c2cf10c..c7f624a 100644 --- a/lib/github/ldap/server.rb +++ b/lib/github/ldap/server.rb @@ -38,6 +38,8 @@ def self.start_server(options = {}) @server_options[:domain] = @server_options[:user_domain] @server_options[:tmpdir] ||= server_tmp + @server_options[:quiet] = false if @server_options[:verbose] + @ldap_server = Ladle::Server.new(@server_options) @ldap_server.start end From 19aa5070c17f71a7eef8eed6ab2b5c398b658bfe Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 11:02:49 -0700 Subject: [PATCH 045/307] Use ENV.fetch with default --- test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 3408a72..57bcf28 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -14,7 +14,7 @@ class GitHub::Ldap::Test < Minitest::Test def self.test_env - ENV['TESTENV'] || "apacheds" + ENV.fetch("TESTENV", "apacheds") end def self.run(reporter, options = {}) From d80dbc55a6769bb8eed9ecd04024ae93b0bfb560 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 11:03:21 -0700 Subject: [PATCH 046/307] Update one Domain test with correct fixture --- test/domain_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 6c0d1e4..e771951 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -7,8 +7,8 @@ def setup end def test_user_valid_login - user = @domain.valid_login?('calavera', 'passworD1') - assert_equal 'uid=calavera,dc=github,dc=com', user.dn + assert user = @domain.valid_login?('user1', 'passworD1') + assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn end def test_user_with_invalid_password From 010fbc4d4559db51e83f79dac5f55c96f9436900 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 14:45:50 -0700 Subject: [PATCH 047/307] Fix Domain posixGroup tests, fixtures --- test/domain_test.rb | 8 ++++---- test/fixtures/common/seed.ldif | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index e771951..ec3e7e4 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -186,11 +186,11 @@ def setup recursive_group_search_fallback: false @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") - @cn = "enterprise-posix-devs" + @cn = "posix-group1" end def test_membership_for_posixGroups - assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind + assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind assert @domain.is_member?(user, [@cn]), "Expected `#{@cn}` to include the member `#{user.dn}`" @@ -206,11 +206,11 @@ def setup posix_support: false # disable posixGroup support @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") - @cn = "enterprise-posix-devs" + @cn = "posix-group1" end def test_membership_for_posixGroups - assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind + assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind refute @domain.is_member?(user, [@cn]), "Expected `#{@cn}` to not include the member `#{user.dn}`" diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif index 3067949..b07aedc 100644 --- a/test/fixtures/common/seed.ldif +++ b/test/fixtures/common/seed.ldif @@ -334,3 +334,14 @@ cn: recursively-nested-groups objectClass: groupOfNames member: cn=head-group,ou=Groups,dc=github,dc=com member: cn=tail-group,ou=Groups,dc=github,dc=com + +# posixGroup + +dn: cn=posix-group1,ou=Groups,dc=github,dc=com +cn: posix-group1 +objectClass: posixGroup +memberUid: user1 +memberUid: user2 +memberUid: user3 +memberUid: user4 +memberUid: user5 From b3ac5f3627d31d0b6d07f799d2b8cc14a58fb9c2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 14:46:22 -0700 Subject: [PATCH 048/307] Add byebug as an development, test bundler dependency --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index ab76291..106eaf9 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,7 @@ source 'https://rubygems.org' # Specify your gem's dependencies in github-ldap.gemspec gemspec + +group :test, :development do + gem "byebug" +end From 1fe748e25687290c51b86ef731956a01de55558a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 14:51:38 -0700 Subject: [PATCH 049/307] Experiment with reusing the ApacheDS LDAP server for each test --- test/test_helper.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 57bcf28..feee10f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,6 +12,15 @@ require 'minitest/autorun' +if ENV.fetch('TESTENV', "apacheds") == "apacheds" + # Make sure we clean up running test server + # NOTE: We need to do this manually since its internal `at_exit` hook + # collides with Minitest's autorun at_exit handling, hence this hook. + Minitest.after_run do + GitHub::Ldap.stop_server + end +end + class GitHub::Ldap::Test < Minitest::Test def self.test_env ENV.fetch("TESTENV", "apacheds") @@ -25,7 +34,8 @@ def self.run(reporter, options = {}) def self.stop_server if test_env == "apacheds" - GitHub::Ldap.stop_server + # see Minitest.after_run hook above. + # GitHub::Ldap.stop_server end end @@ -40,6 +50,9 @@ def self.test_server_options def self.start_server if test_env == "apacheds" + # skip this if a server has already been started + return if GitHub::Ldap.ldap_server + GitHub::Ldap.start_server(test_server_options) end end From 34d63f6f9f740e2a232ac0dcef89ac939d9a9107 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 15:14:28 -0700 Subject: [PATCH 050/307] byebug is supported on MRI 2.0, 2.1 only --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 106eaf9..4abbfe8 100644 --- a/Gemfile +++ b/Gemfile @@ -4,5 +4,5 @@ source 'https://rubygems.org' gemspec group :test, :development do - gem "byebug" + gem "byebug", :platforms => [:mri_20, :mri_21] end From 84e37ce4f64ae89c7c5647d072b9db1545f2ec5c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 16:13:32 -0700 Subject: [PATCH 051/307] Add supporting fixtures, fix email domain --- test/fixtures/common/seed.ldif | 38 +++++++++++++++++++++----------- test/fixtures/openldap/seed.ldif | 22 +++++++++--------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif index b07aedc..e319f69 100644 --- a/test/fixtures/common/seed.ldif +++ b/test/fixtures/common/seed.ldif @@ -31,7 +31,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user1@openldap.ghe.local +mail: user1@github.com dn: uid=user2,ou=People,dc=github,dc=com uid: user2 @@ -42,7 +42,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user2@openldap.ghe.local +mail: user2@github.com dn: uid=user3,ou=People,dc=github,dc=com uid: user3 @@ -53,7 +53,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user3@openldap.ghe.local +mail: user3@github.com dn: uid=user4,ou=People,dc=github,dc=com uid: user4 @@ -64,7 +64,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user4@openldap.ghe.local +mail: user4@github.com dn: uid=user5,ou=People,dc=github,dc=com uid: user5 @@ -75,7 +75,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user5@openldap.ghe.local +mail: user5@github.com dn: uid=user6,ou=People,dc=github,dc=com uid: user6 @@ -86,7 +86,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user6@openldap.ghe.local +mail: user6@github.com dn: uid=user7,ou=People,dc=github,dc=com uid: user7 @@ -97,7 +97,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user7@openldap.ghe.local +mail: user7@github.com dn: uid=user8,ou=People,dc=github,dc=com uid: user8 @@ -108,7 +108,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user8@openldap.ghe.local +mail: user8@github.com dn: uid=user9,ou=People,dc=github,dc=com uid: user9 @@ -119,7 +119,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user9@openldap.ghe.local +mail: user9@github.com dn: uid=user10,ou=People,dc=github,dc=com uid: user10 @@ -130,9 +130,9 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user10@openldap.ghe.local +mail: user10@github.com -# Emailless User 1 +# Emailless User dn: uid=emailless-user1,ou=People,dc=github,dc=com uid: emailless-user1 @@ -144,7 +144,19 @@ objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -# Admin User 1 +# Groupless User + +dn: uid=groupless-user1,ou=People,dc=github,dc=com +uid: groupless-user1 +cn: groupless-user1 +sn: groupless-user1 +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +userPassword: passworD1 + +# Admin User dn: uid=admin1,ou=People,dc=github,dc=com uid: admin1 @@ -155,7 +167,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: admin1@openldap.ghe.local +mail: admin1@github.com # Groups diff --git a/test/fixtures/openldap/seed.ldif b/test/fixtures/openldap/seed.ldif index d0eb715..2a0765f 100644 --- a/test/fixtures/openldap/seed.ldif +++ b/test/fixtures/openldap/seed.ldif @@ -17,7 +17,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user1@openldap.ghe.local +mail: user1@github.com dn: uid=user2,ou=People,dc=github,dc=com uid: user2 @@ -28,7 +28,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user2@openldap.ghe.local +mail: user2@github.com dn: uid=user3,ou=People,dc=github,dc=com uid: user3 @@ -39,7 +39,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user3@openldap.ghe.local +mail: user3@github.com dn: uid=user4,ou=People,dc=github,dc=com uid: user4 @@ -50,7 +50,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user4@openldap.ghe.local +mail: user4@github.com dn: uid=user5,ou=People,dc=github,dc=com uid: user5 @@ -61,7 +61,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user5@openldap.ghe.local +mail: user5@github.com dn: uid=user6,ou=People,dc=github,dc=com uid: user6 @@ -72,7 +72,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user6@openldap.ghe.local +mail: user6@github.com dn: uid=user7,ou=People,dc=github,dc=com uid: user7 @@ -83,7 +83,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user7@openldap.ghe.local +mail: user7@github.com dn: uid=user8,ou=People,dc=github,dc=com uid: user8 @@ -94,7 +94,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user8@openldap.ghe.local +mail: user8@github.com dn: uid=user9,ou=People,dc=github,dc=com uid: user9 @@ -105,7 +105,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user9@openldap.ghe.local +mail: user9@github.com dn: uid=user10,ou=People,dc=github,dc=com uid: user10 @@ -116,7 +116,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: user10@openldap.ghe.local +mail: user10@github.com dn: uid=emailless-user1,ou=People,dc=github,dc=com uid: emailless-user1 @@ -137,7 +137,7 @@ objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: passworD1 -mail: admin1@openldap.ghe.local +mail: admin1@github.com dn: cn=ghe-users,ou=Groups,dc=github,dc=com cn: ghe-users From 919734ea185667b412ca535a342d4d9527ecbefb Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 16:14:42 -0700 Subject: [PATCH 052/307] Use common seed for OpenLDAP --- script/install-openldap | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/install-openldap b/script/install-openldap index bdbeae9..bb0033f 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -3,6 +3,7 @@ set -e set -x BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )" +SEED_PATH="$( cd `dirname $0`/../test/fixtures/common && pwd )" DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils @@ -36,7 +37,7 @@ sudo /etc/init.d/slapd start # Import seed data. # NOTE: use ldapadd in order for memberOf and refint to apply, instead of: # /vagrant/services/ldap/openldap/seed.rb | sudo slapadd -F /etc/ldap/slapd.d -cat $BASE_PATH/seed.ldif | +cat $SEED_PATH/seed.ldif | /usr/bin/time sudo ldapadd -x -D "cn=admin,dc=github,dc=com" -w passworD1 \ -h localhost -p 389 From 46fea0d6bb705e968451be935ab802266176dda1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 16:14:54 -0700 Subject: [PATCH 053/307] Rewrite Domain tests --- test/domain_test.rb | 97 +++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index ec3e7e4..8ddf72c 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -12,8 +12,8 @@ def test_user_valid_login end def test_user_with_invalid_password - assert !@domain.valid_login?('calavera', 'foo'), - "Login `calavera` expected to be invalid with password `foo`" + assert !@domain.valid_login?('user1', 'foo'), + "Login `user1` expected to be invalid with password `foo`" end def test_user_with_invalid_login @@ -22,115 +22,118 @@ def test_user_with_invalid_login end def test_groups_in_server - assert_equal 2, @domain.groups(%w(Enterprise People)).size + assert_equal 2, @domain.groups(%w(ghe-users ghe-admins)).size end def test_user_in_group - user = @domain.valid_login?('calavera', 'passworD1') + assert user = @domain.valid_login?('user1', 'passworD1') - assert @domain.is_member?(user, %w(Enterprise People)), - "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`" + assert @domain.is_member?(user, %w(ghe-users ghe-admins)), + "Expected `ghe-users` or `ghe-admins` to include the member `#{user.dn}`" end def test_user_not_in_different_group - user = @domain.valid_login?('calavera', 'passworD1') + user = @domain.valid_login?('user1', 'passworD1') - assert !@domain.is_member?(user, %w(People)), - "Expected `Poeple` not to include the member `#{user.dn}`" + refute @domain.is_member?(user, %w(ghe-admins)), + "Expected `ghe-admins` not to include the member `#{user.dn}`" end def test_user_without_group - user = @domain.valid_login?('ldaptest', 'secret') + user = @domain.valid_login?('groupless-user1', 'passworD1') - assert !@domain.is_member?(user, %w(People)), - "Expected `People` not to include the member `#{user.dn}`" + assert !@domain.is_member?(user, %w(all-users)), + "Expected `all-users` not to include the member `#{user.dn}`" end - def test_authenticate_doesnt_return_invalid_users - user = @domain.authenticate!('calavera', 'passworD1') - assert_equal 'uid=calavera,dc=github,dc=com', user.dn + def test_authenticate_returns_valid_users + user = @domain.authenticate!('user1', 'passworD1') + assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn end def test_authenticate_doesnt_return_invalid_users - assert !@domain.authenticate!('calavera', 'foo'), + refute @domain.authenticate!('user1', 'foo'), "Expected `authenticate!` to not return an invalid user" end def test_authenticate_check_valid_user_and_groups - user = @domain.authenticate!('calavera', 'passworD1', %w(Enterprise People)) + user = @domain.authenticate!('user1', 'passworD1', %w(ghe-users ghe-admins)) - assert_equal 'uid=calavera,dc=github,dc=com', user.dn + assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn end def test_authenticate_doesnt_return_valid_users_in_different_groups - assert !@domain.authenticate!('calavera', 'passworD1', %w(People)), + refute @domain.authenticate!('user1', 'passworD1', %w(ghe-admins)), "Expected `authenticate!` to not return an user" end def test_membership_empty_for_non_members - user = @ldap.domain('uid=calavera,dc=github,dc=com').bind + user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind - assert @domain.membership(user, %w(People)).empty?, - "Expected `calavera` not to be a member of `People`." + assert @domain.membership(user, %w(ghe-admins)).empty?, + "Expected `user1` not to be a member of `ghe-admins`." end def test_membership_groups_for_members - user = @ldap.domain('uid=calavera,dc=github,dc=com').bind - groups = @domain.membership(user, %w(Enterprise People)) + user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind + groups = @domain.membership(user, %w(ghe-users ghe-admins)) assert_equal 1, groups.size - assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn + assert_equal 'cn=ghe-users,ou=Groups,dc=github,dc=com', groups.first.dn end def test_membership_with_virtual_attributes ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true)) - user = ldap.domain('uid=calavera,dc=github,dc=com').bind - user[:memberof] = 'cn=Enterprise,ou=Group,dc=github,dc=com' + + user = ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind + user[:memberof] = 'cn=ghe-admins,ou=Groups,dc=github,dc=com' domain = ldap.domain("dc=github,dc=com") - groups = domain.membership(user, %w(Enterprise People)) + groups = domain.membership(user, %w(ghe-admins)) assert_equal 1, groups.size - assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn + assert_equal 'cn=ghe-admins,ou=Groups,dc=github,dc=com', groups.first.dn end def test_search assert 1, @domain.search( attributes: %w(uid), - filter: Net::LDAP::Filter.eq('uid', 'calavera')).size + filter: Net::LDAP::Filter.eq('uid', 'user1')).size end def test_search_override_base_name assert 1, @domain.search( base: "this base name is incorrect", attributes: %w(uid), - filter: Net::LDAP::Filter.eq('uid', 'calavera')).size + filter: Net::LDAP::Filter.eq('uid', 'user1')).size end def test_user_exists - assert_equal 'uid=calavera,dc=github,dc=com', @domain.user?('calavera').dn + assert user = @domain.user?('user1') + assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn end def test_user_wildcards_are_filtered - assert !@domain.user?('cal*'), 'Expected uid `cal*` to not complete' + refute @domain.user?('user*'), 'Expected uid `user*` to not complete' end def test_user_does_not_exist - assert !@domain.user?('foobar'), 'Expected uid `foobar` to not exist.' + refute @domain.user?('foobar'), 'Expected uid `foobar` to not exist.' end def test_user_returns_every_attribute - assert_equal ['calavera@github.com'], @domain.user?('calavera')[:mail] + assert user = @domain.user?('user1') + assert_equal ['user1@github.com'], user[:mail] end def test_auth_binds - user = @domain.user?('calavera') - assert @domain.auth(user, 'passworD1'), 'Expected user to be bound.' + assert user = @domain.user?('user1') + assert @domain.auth(user, 'passworD1'), 'Expected user to bind' end def test_auth_does_not_bind - user = @domain.user?('calavera') - assert !@domain.auth(user, 'foo'), 'Expected user not to be bound.' + assert user = @domain.user?('user1') + refute @domain.auth(user, 'foo'), 'Expected user not not bind' end end @@ -149,17 +152,17 @@ def setup end def test_membership_in_subgroups - user = @ldap.domain('uid=rubiojr,ou=users,dc=github,dc=com').bind + user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind - assert @domain.is_member?(user, %w(enterprise-ops)), - "Expected `enterprise-ops` to include the member `#{user.dn}`" + assert @domain.is_member?(user, %w(nested-groups)), + "Expected `nested-groups` to include the member `#{user.dn}`" end def test_membership_in_deeply_nested_subgroups - assert user = @ldap.domain('uid=user1.1.1.1,ou=users,dc=github,dc=com').bind + assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind - assert @domain.is_member?(user, %w(group1)), - "Expected `group1` to include the member `#{user.dn}` via deep recursion" + assert @domain.is_member?(user, %w(n-depth-nested-group4)), + "Expected `n-depth-nested-group4` to include the member `#{user.dn}` via deep recursion" end end @@ -169,11 +172,11 @@ def setup recursive_group_search_fallback: true @ldap = GitHub::Ldap.new(opts) @domain = @ldap.domain("dc=github,dc=com") - @cn = "enterprise-posix-devs" + @cn = "posix-group1" end def test_membership_for_posixGroups - assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind + assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind assert @domain.is_member?(user, [@cn]), "Expected `#{@cn}` to include the member `#{user.dn}`" From a1988abf64df0cd49ae56d358fa97a8247435951 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 16:35:00 -0700 Subject: [PATCH 054/307] Include gidNumber in seed; fix posixGroup schema The original posixGroup schema was mostly made up, so this duplicates the reference: http://www.zytrax.com/books/ldap/ape/nis.html --- test/fixtures/common/seed.ldif | 1 + test/fixtures/posixGroup.schema.ldif | 42 ++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif index e319f69..e624858 100644 --- a/test/fixtures/common/seed.ldif +++ b/test/fixtures/common/seed.ldif @@ -352,6 +352,7 @@ member: cn=tail-group,ou=Groups,dc=github,dc=com dn: cn=posix-group1,ou=Groups,dc=github,dc=com cn: posix-group1 objectClass: posixGroup +gidNumber: 1001 memberUid: user1 memberUid: user2 memberUid: user3 diff --git a/test/fixtures/posixGroup.schema.ldif b/test/fixtures/posixGroup.schema.ldif index 94dd488..3ba04e0 100644 --- a/test/fixtures/posixGroup.schema.ldif +++ b/test/fixtures/posixGroup.schema.ldif @@ -1,26 +1,52 @@ version: 1 -dn: m-oid=1.3.6.1.4.1.18055.0.4.1.2.1001,ou=attributeTypes,cn=other,ou=schema +# attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber' +# DESC 'An integer uniquely identifying a group in an administrative domain' +# EQUALITY integerMatch +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +dn: m-oid=1.3.6.1.1.1.1.1,ou=attributeTypes,cn=other,ou=schema +objectClass: metaAttributeType +objectClass: metaTop +objectClass: top +m-collective: FALSE +m-description: An integer uniquely identifying a group in an administrative domain +m-equality: integerMatch +m-name: gidNumber +m-syntax: 1.3.6.1.4.1.1466.115.121.1.27 +m-usage: USER_APPLICATIONS +m-oid: 1.3.6.1.1.1.1.1 + +# attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid' +# EQUALITY caseExactIA5Match +# SUBSTR caseExactIA5SubstringsMatch +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +dn: m-oid=1.3.6.1.1.1.1.12,ou=attributeTypes,cn=other,ou=schema objectClass: metaAttributeType objectClass: metaTop objectClass: top m-collective: FALSE m-description: memberUid -m-equality: caseExactMatch +m-equality: caseExactIA5Match m-name: memberUid -m-syntax: 1.3.6.1.4.1.1466.115.121.1.15 +m-syntax: 1.3.6.1.4.1.1466.115.121.1.26 m-usage: USER_APPLICATIONS -m-oid: 1.3.6.1.4.1.18055.0.4.1.2.1001 +m-oid: 1.3.6.1.1.1.1.12 -dn: m-oid=1.3.6.1.4.1.18055.0.4.1.3.1001,ou=objectClasses,cn=other,ou=schema +# objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top STRUCTURAL +# DESC 'Abstraction of a group of accounts' +# MUST ( cn $ gidNumber ) +# MAY ( userPassword $ memberUid $ description ) ) +dn: m-oid=1.3.6.1.1.1.2.2,ou=objectClasses,cn=other,ou=schema objectClass: metaObjectClass objectClass: metaTop objectClass: top m-description: posixGroup -m-may: cn -m-may: sn +m-must: cn +m-must: gidNumber m-may: memberUid +m-may: userPassword +m-may: description m-supobjectclass: top m-name: posixGroup -m-oid: 1.3.6.1.4.1.18055.0.4.1.3.1001 +m-oid: 1.3.6.1.1.1.2.2 m-typeobjectclass: STRUCTURAL From 102351c53df6ddf0de154083c18ecb7cbf4a5c49 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 16:44:49 -0700 Subject: [PATCH 055/307] Rewrite GitHub::Ldap tests --- test/ldap_test.rb | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 27861d3..40fcb95 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -22,59 +22,54 @@ def test_start_tls end def test_search_delegator - @ldap.domain('dc=github,dc=com').valid_login? 'calavera', 'secret' + assert user = @ldap.domain('dc=github,dc=com').valid_login?('user1', 'passworD1') - result = @ldap.search( - {:base => 'dc=github,dc=com', - :attributes => %w(uid), - :filter => Net::LDAP::Filter.eq('uid', 'calavera')}) + result = @ldap.search \ + :base => 'dc=github,dc=com', + :attributes => %w(uid), + :filter => Net::LDAP::Filter.eq('uid', 'user1') refute result.empty? - assert_equal 'calavera', result.first[:uid].first + assert_equal 'user1', result.first[:uid].first end - def test_virtual_attributes_defaults - @ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true)) - - assert @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults" - assert_equal 'memberOf', @ldap.virtual_attributes.virtual_membership + def test_virtual_attributes_disabled + refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled" end - def test_virtual_attributes_defaults + def test_virtual_attributes_configured ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true)) - assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults" + assert ldap.virtual_attributes.enabled?, + "Expected virtual attributes to be enabled" assert_equal 'memberOf', ldap.virtual_attributes.virtual_membership end - def test_virtual_attributes_hash + def test_virtual_attributes_configured_with_membership_attribute ldap = GitHub::Ldap.new(options.merge(virtual_attributes: {virtual_membership: "isMemberOf"})) - assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults" + assert ldap.virtual_attributes.enabled?, + "Expected virtual attributes to be enabled" assert_equal 'isMemberOf', ldap.virtual_attributes.virtual_membership end - def test_virtual_attributes_disabled - refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled" - end - def test_search_domains ldap = GitHub::Ldap.new(options.merge(search_domains: ['dc=github,dc=com'])) - result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'calavera')) + result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'user1')) refute result.empty? - assert_equal 'calavera', result.first[:uid].first + assert_equal 'user1', result.first[:uid].first end def test_instruments_search events = @service.subscribe "search.github_ldap" - result = @ldap.search(filter: "(uid=calavera)", :base => "dc=github,dc=com") + result = @ldap.search(filter: "(uid=user1)", :base => "dc=github,dc=com") refute_predicate result, :empty? payload, event_result = events.pop assert payload assert event_result assert_equal result, event_result - assert_equal "(uid=calavera)", payload[:filter].to_s + assert_equal "(uid=user1)", payload[:filter].to_s assert_equal "dc=github,dc=com", payload[:base] end end From 6502ab1026d43d631780322c819f4af547ee4366 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 6 Oct 2014 17:05:21 -0700 Subject: [PATCH 056/307] Fix Group tests, add fixture group with missing members --- test/fixtures/common/seed.ldif | 9 +++++++++ test/group_test.rb | 28 +++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif index e624858..29284bb 100644 --- a/test/fixtures/common/seed.ldif +++ b/test/fixtures/common/seed.ldif @@ -358,3 +358,12 @@ memberUid: user2 memberUid: user3 memberUid: user4 memberUid: user5 + +# missing members + +dn: cn=missing-users,ou=Groups,dc=github,dc=com +cn: missing-users +objectClass: groupOfNames +member: uid=user1,ou=People,dc=github,dc=com +member: uid=user2,ou=People,dc=github,dc=com +member: uid=nonexistent-user,ou=People,dc=github,dc=com diff --git a/test/group_test.rb b/test/group_test.rb index 2bde806..1ed5f82 100644 --- a/test/group_test.rb +++ b/test/group_test.rb @@ -2,12 +2,12 @@ class GitHubLdapGroupTest < GitHub::Ldap::Test def groups_domain - @ldap.domain("ou=groups,dc=github,dc=com") + @ldap.domain("ou=Groups,dc=github,dc=com") end def setup @ldap = GitHub::Ldap.new(options) - @group = @ldap.group("cn=enterprise,ou=groups,dc=github,dc=com") + @group = @ldap.group("cn=ghe-users,ou=Groups,dc=github,dc=com") end def test_group? @@ -21,34 +21,36 @@ def test_group? end def test_subgroups - assert_equal 3, @group.subgroups.size + group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com") + assert_equal 2, group.subgroups.size end def test_members_from_subgroups - assert_equal 4, @group.members.size + group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com") + assert_equal 10, group.members.size end def test_all_domain_groups groups = groups_domain.all_groups - assert_equal 8, groups.size + assert_equal 27, groups.size end def test_filter_domain_groups - groups = groups_domain.filter_groups('devs') + groups = groups_domain.filter_groups('ghe-users') assert_equal 1, groups.size end def test_filter_domain_groups_limited groups = [] - groups_domain.filter_groups('enter', size: 1) do |entry| + groups_domain.filter_groups('deeply-nested-group', size: 1) do |entry| groups << entry end assert_equal 1, groups.size end def test_filter_domain_groups_unlimited - groups = groups_domain.filter_groups('ent') - assert_equal 3, groups.size + groups = groups_domain.filter_groups('deeply-nested-group') + assert_equal 5, groups.size end def test_unknown_group @@ -59,11 +61,11 @@ def test_unknown_group class GitHubLdapLoopedGroupTest < GitHub::Ldap::Test def setup - @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com") + @group = GitHub::Ldap.new(options).group("cn=recursively-nested-groups,ou=Groups,dc=github,dc=com") end def test_members_from_subgroups - assert_equal 4, @group.members.size + assert_equal 10, @group.members.size end end @@ -73,10 +75,10 @@ def setup end def test_load_right_members - assert_equal 3, @ldap.domain("cn=spaniards,ou=groups,dc=github,dc=com").bind[:member].size + assert_equal 3, @ldap.domain("cn=missing-users,ou=groups,dc=github,dc=com").bind[:member].size end def test_ignore_missing_member_entries - assert_equal 2, @ldap.group("cn=spaniards,ou=groups,dc=github,dc=com").members.size + assert_equal 2, @ldap.group("cn=missing-users,ou=groups,dc=github,dc=com").members.size end end From 15953cc66634f5ffa58f2534215ae06f8feac948 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 8 Oct 2014 01:30:41 -0700 Subject: [PATCH 057/307] Fix up posixGroup tests --- test/posix_group_test.rb | 49 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/test/posix_group_test.rb b/test/posix_group_test.rb index 4f9d815..e21b3ac 100644 --- a/test/posix_group_test.rb +++ b/test/posix_group_test.rb @@ -3,48 +3,49 @@ class GitHubLdapPosixGroupTest < GitHub::Ldap::Test def setup @simple_group = Net::LDAP::Entry._load(""" -dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com -cn: enterprise-posix-devs +dn: cn=simple-group,ou=Groups,dc=github,dc=com +cn: simple-group objectClass: posixGroup -memberUid: benburkert -memberUid: mtodd""") +memberUid: user1 +memberUid: user2""") @one_level_deep_group = Net::LDAP::Entry._load(""" -dn: cn=enterprise-posix-ops,ou=groups,dc=github,dc=com -cn: enterprise-posix-ops +dn: cn=one-level-deep-group,ou=Groups,dc=github,dc=com +cn: one-level-deep-group objectClass: posixGroup objectClass: groupOfNames -memberUid: sbryant -member: cn=spaniards,ou=groups,dc=github,dc=com""") +memberUid: user6 +member: cn=ghe-users,ou=Groups,dc=github,dc=com""") @two_levels_deep_group = Net::LDAP::Entry._load(""" -dn: cn=enterprise-posix,ou=groups,dc=github,dc=com -cn: Enterprise Posix +dn: cn=two-levels-deep-group,ou=Groups,dc=github,dc=com +cn: two-levels-deep-group objectClass: posixGroup objectClass: groupOfNames -memberUid: calavera -member: cn=enterprise-devs,ou=groups,dc=github,dc=com -member: cn=enterprise-ops,ou=groups,dc=github,dc=com""") +memberUid: user6 +member: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com +member: cn=posix-group1,ou=Groups,dc=github,dc=com""") @empty_group = Net::LDAP::Entry._load(""" -dn: cn=enterprise-posix-empty,ou=groups,dc=github,dc=com -cn: enterprise-posix-empty +dn: cn=empty-group,ou=Groups,dc=github,dc=com +cn: empty-group objectClass: posixGroup""") @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) end def test_posix_group - assert GitHub::Ldap::PosixGroup.valid?(@simple_group), + entry = @ldap.search(filter: "(cn=posix-group1)").first + assert GitHub::Ldap::PosixGroup.valid?(entry), "Expected entry to be a valid posixGroup" end def test_posix_simple_members - group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group) + assert group = @ldap.group("cn=posix-group1,ou=Groups,dc=github,dc=com") members = group.members - assert_equal 2, members.size - assert_equal %w(benburkert mtodd), members.map(&:uid).flatten.sort + assert_equal 5, members.size + assert_equal %w(user1 user2 user3 user4 user5), members.map(&:uid).flatten.sort end def test_posix_combined_group @@ -58,7 +59,7 @@ def test_posix_combined_group_unique_members group = GitHub::Ldap::PosixGroup.new(@ldap, @two_levels_deep_group) members = group.members - assert_equal 4, members.size + assert_equal 10, members.size end def test_empty_subgroups @@ -77,7 +78,7 @@ def test_posix_combined_group_subgroups def test_is_member_simple_group group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group) - user = @ldap.domain("uid=benburkert,ou=users,dc=github,dc=com").bind + user = @ldap.domain("uid=user1,ou=People,dc=github,dc=com").bind assert group.is_member?(user), "Expected user in the memberUid list to be a member of the posixgroup" @@ -85,7 +86,7 @@ def test_is_member_simple_group def test_is_member_combined_group group = GitHub::Ldap::PosixGroup.new(@ldap, @one_level_deep_group) - user = @ldap.domain("uid=calavera,ou=users,dc=github,dc=com").bind + user = @ldap.domain("uid=user1,ou=People,dc=github,dc=com").bind assert group.is_member?(user), "Expected user in a subgroup to be a member of the posixgroup" @@ -93,7 +94,7 @@ def test_is_member_combined_group def test_is_not_member_simple_group group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group) - user = @ldap.domain("uid=calavera,ou=users,dc=github,dc=com").bind + user = @ldap.domain("uid=user10,ou=People,dc=github,dc=com").bind refute group.is_member?(user), "Expected user to not be member when her uid is not in the list of memberUid" @@ -101,7 +102,7 @@ def test_is_not_member_simple_group def test_is_member_combined_group group = GitHub::Ldap::PosixGroup.new(@ldap, @one_level_deep_group) - user = @ldap.domain("uid=benburkert,ou=users,dc=github,dc=com").bind + user = @ldap.domain("uid=user10,ou=People,dc=github,dc=com").bind refute group.is_member?(user), "Expected user to not be member when she's not member of any subgroup" From 70142997d2a36aa33002cc56ee4c39d155bce4df Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 8 Oct 2014 01:59:34 -0700 Subject: [PATCH 058/307] Update membership validator tests --- test/membership_validators/classic_test.rb | 50 +++----------- test/membership_validators/recursive_test.rb | 73 ++++---------------- 2 files changed, 23 insertions(+), 100 deletions(-) diff --git a/test/membership_validators/classic_test.rb b/test/membership_validators/classic_test.rb index 7802826..9b2c32b 100644 --- a/test/membership_validators/classic_test.rb +++ b/test/membership_validators/classic_test.rb @@ -1,16 +1,10 @@ require_relative '../test_helper' class GitHubLdapClassicMembershipValidatorsTest < GitHub::Ldap::Test - def self.test_server_options - { search_domains: "dc=github,dc=com", - user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s - } - end - def setup - @ldap = GitHub::Ldap.new(options) + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1.1.1.1') + @entry = @domain.user?('user1') @validator = GitHub::Ldap::MembershipValidators::Classic end @@ -20,60 +14,38 @@ def make_validator(groups) end def test_validates_user_in_group - validator = make_validator(%w(group1.1.1.1)) + validator = make_validator(%w(nested-group1)) assert validator.perform(@entry) end def test_validates_user_in_child_group - validator = make_validator(%w(group1.1.1)) + validator = make_validator(%w(n-depth-nested-group1)) assert validator.perform(@entry) end def test_validates_user_in_grandchild_group - validator = make_validator(%w(group1.1)) + validator = make_validator(%w(n-depth-nested-group2)) assert validator.perform(@entry) end def test_validates_user_in_great_grandchild_group - validator = make_validator(%w(group1)) + validator = make_validator(%w(n-depth-nested-group3)) assert validator.perform(@entry) end def test_does_not_validate_user_not_in_group - validator = make_validator(%w(Enterprise)) + validator = make_validator(%w(ghe-admins)) refute validator.perform(@entry) end def test_does_not_validate_user_not_in_any_group - @entry = @domain.user?('admin') - validator = make_validator(%w(Enterprise)) + @entry = @domain.user?('groupless-user1') + validator = make_validator(%w(all-users)) refute validator.perform(@entry) end -end - -class GitHubLdapClassicMembershipValidatorsWithPosixGroupsTest < GitHub::Ldap::Test - def self.test_server_options - { search_domains: "dc=github,dc=com", - uid: "uid", - custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), - user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s - } - end - - def setup - @ldap = GitHub::Ldap.new(options) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1.1.1.1') - @validator = GitHub::Ldap::MembershipValidators::Classic - end - - def make_validator(groups) - groups = @domain.groups(groups) - @validator.new(@ldap, groups) - end - def test_validates_user_in_group - validator = make_validator(%w(group1.1.1.1)) + def test_validates_user_in_posix_group + validator = make_validator(%w(posix-group1)) assert validator.perform(@entry) end end diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb index fd8e7ee..e351532 100644 --- a/test/membership_validators/recursive_test.rb +++ b/test/membership_validators/recursive_test.rb @@ -1,17 +1,10 @@ require_relative '../test_helper' class GitHubLdapRecursiveMembershipValidatorsTest < GitHub::Ldap::Test - def self.test_server_options - { search_domains: "dc=github,dc=com", - uid: "uid", - user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s - } - end - def setup - @ldap = GitHub::Ldap.new(options) + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1.1.1.1') + @entry = @domain.user?('user1') @validator = GitHub::Ldap::MembershipValidators::Recursive end @@ -21,85 +14,43 @@ def make_validator(groups) end def test_validates_user_in_group - validator = make_validator(%w(group1.1.1.1)) + validator = make_validator(%w(nested-group1)) assert validator.perform(@entry) end def test_validates_user_in_child_group - validator = make_validator(%w(group1.1.1)) + validator = make_validator(%w(n-depth-nested-group1)) assert validator.perform(@entry) end def test_validates_user_in_grandchild_group - validator = make_validator(%w(group1.1)) + validator = make_validator(%w(n-depth-nested-group2)) assert validator.perform(@entry) end def test_validates_user_in_great_grandchild_group - validator = make_validator(%w(group1)) + validator = make_validator(%w(n-depth-nested-group3)) assert validator.perform(@entry) end def test_does_not_validate_user_in_great_granchild_group_with_depth - validator = make_validator(%w(group1)) + validator = make_validator(%w(n-depth-nested-group3)) refute validator.perform(@entry, 2) end def test_does_not_validate_user_not_in_group - validator = make_validator(%w(Enterprise)) + validator = make_validator(%w(ghe-admins)) refute validator.perform(@entry) end def test_does_not_validate_user_not_in_any_group - @entry = @domain.user?('admin') - validator = make_validator(%w(Enterprise)) + @entry = @domain.user?('groupless-user1') + validator = make_validator(%w(all-users)) refute validator.perform(@entry) end -end - -class GitHubLdapRecursiveMembershipValidatorsWithPosixGroupsTest < GitHub::Ldap::Test - def self.test_server_options - { search_domains: "dc=github,dc=com", - uid: "uid", - custom_schemas: FIXTURES.join('posixGroup.schema.ldif'), - user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s - } - end - - def setup - @ldap = GitHub::Ldap.new(options) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1.1.1.1') - @validator = GitHub::Ldap::MembershipValidators::Recursive - end - - def make_validator(groups) - groups = @domain.groups(groups) - @validator.new(@ldap, groups) - end - - def test_validates_user_in_group - validator = make_validator(%w(group1.1.1.1)) - assert validator.perform(@entry) - end - - def test_validates_user_in_child_group - validator = make_validator(%w(group1.1.1)) - assert validator.perform(@entry) - end - - def test_validates_user_in_grandchild_group - validator = make_validator(%w(group1.1)) - assert validator.perform(@entry) - end - def test_validates_user_in_great_grandchild_group - validator = make_validator(%w(group1)) + def test_validates_user_in_posix_group + validator = make_validator(%w(posix-group1)) assert validator.perform(@entry) end - - def test_does_not_validate_user_in_great_granchild_group_with_depth - validator = make_validator(%w(group1)) - refute validator.perform(@entry, 2) - end end From 108d29081b136aeb5bb794901da45e4b6b374c8a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 8 Oct 2014 02:03:22 -0700 Subject: [PATCH 059/307] Include general membership validator test cases --- test/membership_validators_test.rb | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/test/membership_validators_test.rb b/test/membership_validators_test.rb index 77f0358..e1a981f 100644 --- a/test/membership_validators_test.rb +++ b/test/membership_validators_test.rb @@ -7,18 +7,18 @@ def make_validator(groups) end def test_validates_user_in_group - validator = make_validator(%w(Enterprise)) + validator = make_validator(%w(ghe-users)) assert validator.perform(@entry) end def test_does_not_validate_user_not_in_group - validator = make_validator(%w(People)) + validator = make_validator(%w(ghe-admins)) refute validator.perform(@entry) end def test_does_not_validate_user_not_in_any_group - @entry = @domain.user?('ldaptest') - validator = make_validator(%w(Enterprise People)) + @entry = @domain.user?('groupless-user1') + validator = make_validator(%w(ghe-users ghe-admins)) refute validator.perform(@entry) end end @@ -26,16 +26,10 @@ def test_does_not_validate_user_not_in_any_group class GitHubLdapMembershipValidatorsClassicTest < GitHub::Ldap::Test include GitHubLdapMembershipValidatorsTestCases - def self.test_server_options - { search_domains: "dc=github,dc=com", - uid: "uid" - } - end - def setup - @ldap = GitHub::Ldap.new(options) + @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('calavera') + @entry = @domain.user?('user1') @validator = GitHub::Ldap::MembershipValidators::Classic end end @@ -43,16 +37,10 @@ def setup class GitHubLdapMembershipValidatorsRecursiveTest < GitHub::Ldap::Test include GitHubLdapMembershipValidatorsTestCases - def self.test_server_options - { search_domains: "dc=github,dc=com", - uid: "uid" - } - end - def setup - @ldap = GitHub::Ldap.new(options) + @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('calavera') + @entry = @domain.user?('user1') @validator = GitHub::Ldap::MembershipValidators::Recursive end end From 655f23111cdedc41c277551c1b01ac723672a575 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Wed, 15 Oct 2014 15:21:38 -0700 Subject: [PATCH 060/307] add failing test --- test/domain_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/domain_test.rb b/test/domain_test.rb index 8ddf72c..797f716 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -126,6 +126,11 @@ def test_user_returns_every_attribute assert_equal ['user1@github.com'], user[:mail] end + def test_user_returns_subset_of_attributes + assert entry = @domain.user?('user1', :attributes => [:cn]) + assert_equal [:dn, :cn], entry.attribute_names + end + def test_auth_binds assert user = @domain.user?('user1') assert @domain.auth(user, 'passworD1'), 'Expected user to bind' From e132db4c9866dea311db1527783650fe01a00745 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Wed, 15 Oct 2014 15:22:17 -0700 Subject: [PATCH 061/307] pass through search options --- lib/github/ldap/domain.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index 8085c44..aa2066b 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -110,11 +110,15 @@ def valid_login?(login, password) # Check if a user exists based in the `uid`. # # login: is the user's login + # search_options: Net::LDAP#search compatible options to pass through # # Returns the user if the login matches any `uid`. # Returns nil if there are no matches. - def user?(login) - search(filter: login_filter(@uid, login), size: 1).first + def user?(login, search_options = {}) + options = search_options.merge \ + filter: login_filter(@uid, login), + size: 1 + search(options).first end # Check if a user can be bound with a password. From 5ec9b3699d68f10fa1710a8607d543a45daaaa05 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 15 Oct 2014 15:50:22 -0700 Subject: [PATCH 062/307] :fire: unneeded LDIF fixtures --- .../github-with-looped-subgroups.ldif | 82 ----- .../fixtures/github-with-missing-entries.ldif | 85 ----- test/fixtures/github-with-posixGroups.ldif | 105 ------ test/fixtures/github-with-subgroups.ldif | 146 -------- test/fixtures/openldap/seed.ldif | 316 ------------------ 5 files changed, 734 deletions(-) delete mode 100644 test/fixtures/github-with-looped-subgroups.ldif delete mode 100644 test/fixtures/github-with-missing-entries.ldif delete mode 100644 test/fixtures/github-with-posixGroups.ldif delete mode 100644 test/fixtures/github-with-subgroups.ldif delete mode 100644 test/fixtures/openldap/seed.ldif diff --git a/test/fixtures/github-with-looped-subgroups.ldif b/test/fixtures/github-with-looped-subgroups.ldif deleted file mode 100644 index 02868fe..0000000 --- a/test/fixtures/github-with-looped-subgroups.ldif +++ /dev/null @@ -1,82 +0,0 @@ -version: 1 - -# Admin user - -dn: uid=admin,dc=github,dc=com -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -cn: system administrator -sn: administrator -displayName: Directory Superuser -uid: admin -userPassword: secret - -# Groups - -dn: ou=groups,dc=github,dc=com -objectclass: organizationalUnit - -dn: cn=enterprise,ou=groups,dc=github,dc=com -cn: Enterprise -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: cn=enterprise-devs,ou=groups,dc=github,dc=com -member: cn=enterprise-ops,ou=groups,dc=github,dc=com - -dn: cn=enterprise-devs,ou=groups,dc=github,dc=com -cn: enterprise-devs -objectClass: groupOfNames -member: uid=benburkert,ou=users,dc=github,dc=com -member: cn=enterprise,ou=groups,dc=github,dc=com - -dn: cn=enterprise-ops,ou=groups,dc=github,dc=com -cn: enterprise-ops -objectClass: groupOfNames -member: uid=sbryant,ou=users,dc=github,dc=com -member: cn=spaniards,ou=groups,dc=github,dc=com - -dn: cn=spaniards,ou=groups,dc=github,dc=com -cn: spaniards -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: uid=rubiojr,ou=users,dc=github,dc=com - -# Users - -dn: ou=users,dc=github,dc=com -objectclass: organizationalUnit - -dn: uid=calavera,ou=users,dc=github,dc=com -cn: David Calavera -cn: David -sn: Calavera -uid: calavera -userPassword: passworD1 -mail: calavera@github.com -objectClass: inetOrgPerson - -dn: uid=benburkert,ou=users,dc=github,dc=com -cn: benburkert -sn: benburkert -uid: benburkert -userPassword: passworD1 -mail: benburkert@github.com -objectClass: inetOrgPerson - -dn: uid=sbryant,ou=users,dc=github,dc=com -cn: sbryant -sn: sbryant -uid: sbryant -userPassword: passworD1 -mail: sbryant@github.com -objectClass: inetOrgPerson - -dn: uid=rubiojr,ou=users,dc=github,dc=com -cn: rubiojr -sn: rubiojr -uid: rubiojr -userPassword: passworD1 -mail: rubiojr@github.com -objectClass: inetOrgPerson diff --git a/test/fixtures/github-with-missing-entries.ldif b/test/fixtures/github-with-missing-entries.ldif deleted file mode 100644 index be8d316..0000000 --- a/test/fixtures/github-with-missing-entries.ldif +++ /dev/null @@ -1,85 +0,0 @@ -version: 1 - -# Admin user - -dn: uid=admin,dc=github,dc=com -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -cn: system administrator -sn: administrator -displayName: Directory Superuser -uid: admin -userPassword: secret - -# Groups - -dn: ou=groups,dc=github,dc=com -objectclass: organizationalUnit - -dn: cn=enterprise,ou=groups,dc=github,dc=com -cn: Enterprise -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: cn=enterprise-devs,ou=groups,dc=github,dc=com -member: cn=enterprise-ops,ou=groups,dc=github,dc=com - -dn: cn=enterprise-devs,ou=groups,dc=github,dc=com -cn: enterprise-devs -objectClass: groupOfNames -member: uid=benburkert,ou=users,dc=github,dc=com -member: cn=enterprise,ou=groups,dc=github,dc=com - -dn: cn=enterprise-ops,ou=groups,dc=github,dc=com -cn: enterprise-ops -objectClass: groupOfNames -member: uid=sbryant,ou=users,dc=github,dc=com -member: cn=spaniards,ou=groups,dc=github,dc=com - -# The last member of this group is missing on purpose. -# See: https://github.com/github/github-ldap/pull/18 -dn: cn=spaniards,ou=groups,dc=github,dc=com -cn: spaniards -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: uid=rubiojr,ou=users,dc=github,dc=com -member: uid=felipe,ou=users,dc=github,dc=com - -# Users - -dn: ou=users,dc=github,dc=com -objectclass: organizationalUnit - -dn: uid=calavera,ou=users,dc=github,dc=com -cn: David Calavera -cn: David -sn: Calavera -uid: calavera -userPassword: passworD1 -mail: calavera@github.com -objectClass: inetOrgPerson - -dn: uid=benburkert,ou=users,dc=github,dc=com -cn: benburkert -sn: benburkert -uid: benburkert -userPassword: passworD1 -mail: benburkert@github.com -objectClass: inetOrgPerson - -dn: uid=sbryant,ou=users,dc=github,dc=com -cn: sbryant -sn: sbryant -uid: sbryant -userPassword: passworD1 -mail: sbryant@github.com -objectClass: inetOrgPerson - -dn: uid=rubiojr,ou=users,dc=github,dc=com -cn: rubiojr -sn: rubiojr -uid: rubiojr -userPassword: passworD1 -mail: rubiojr@github.com -objectClass: inetOrgPerson diff --git a/test/fixtures/github-with-posixGroups.ldif b/test/fixtures/github-with-posixGroups.ldif deleted file mode 100644 index 5269784..0000000 --- a/test/fixtures/github-with-posixGroups.ldif +++ /dev/null @@ -1,105 +0,0 @@ -version: 1 - -# Admin user - -dn: uid=admin,dc=github,dc=com -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -cn: system administrator -sn: administrator -displayName: Directory Superuser -uid: admin -userPassword: secret - -# Groups - -dn: ou=groups,dc=github,dc=com -objectclass: organizationalUnit -ou: groups - -# Posix Groups - -dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com -cn: enterprise-posix-devs -objectClass: posixGroup -memberUid: benburkert -memberUid: mtodd - -dn: cn=group1,ou=groups,dc=github,dc=com -cn: group1 -objectClass: posixGroup -memberUid: group1.1 -memberUid: user1 - -dn: cn=group1.1,ou=groups,dc=github,dc=com -cn: group1.1 -objectClass: posixGroup -memberUid: group1.1.1 -memberUid: user1.1 - -dn: cn=group1.1.1,ou=groups,dc=github,dc=com -cn: group1.1.1 -objectClass: posixGroup -memberUid: group1.1.1.1 -memberUid: user1.1.1 - -dn: cn=group1.1.1.1,ou=groups,dc=github,dc=com -cn: group1.1.1.1 -objectClass: posixGroup -memberUid: user1.1.1.1 - -# Users - -dn: ou=users,dc=github,dc=com -objectclass: organizationalUnit -ou: users - -dn: uid=benburkert,ou=users,dc=github,dc=com -cn: benburkert -sn: benburkert -uid: benburkert -userPassword: passworD1 -mail: benburkert@github.com -objectClass: inetOrgPerson - -dn: uid=mtodd,ou=users,dc=github,dc=com -cn: mtodd -sn: mtodd -uid: mtodd -userPassword: passworD1 -mail: mtodd@github.com -objectClass: inetOrgPerson - -dn: uid=user1,ou=users,dc=github,dc=com -cn: user1 -sn: user1 -uid: user1 -userPassword: passworD1 -mail: user1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1,ou=users,dc=github,dc=com -cn: user1.1 -sn: user1.1 -uid: user1.1 -userPassword: passworD1 -mail: user1.1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1.1,ou=users,dc=github,dc=com -cn: user1.1.1 -sn: user1.1.1 -uid: user1.1.1 -userPassword: passworD1 -mail: user1.1.1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1.1.1,ou=users,dc=github,dc=com -cn: user1.1.1.1 -sn: user1.1.1.1 -uid: user1.1.1.1 -userPassword: passworD1 -mail: user1.1.1.1@github.com -objectClass: inetOrgPerson diff --git a/test/fixtures/github-with-subgroups.ldif b/test/fixtures/github-with-subgroups.ldif deleted file mode 100644 index c432edc..0000000 --- a/test/fixtures/github-with-subgroups.ldif +++ /dev/null @@ -1,146 +0,0 @@ -version: 1 - -# Admin user - -dn: uid=admin,dc=github,dc=com -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -cn: system administrator -sn: administrator -displayName: Directory Superuser -uid: admin -userPassword: secret - -# Groups - -dn: ou=groups,dc=github,dc=com -objectclass: organizationalUnit -ou: groups - -dn: cn=enterprise,ou=groups,dc=github,dc=com -cn: Enterprise -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: cn=enterprise-devs,ou=groups,dc=github,dc=com -member: cn=enterprise-ops,ou=groups,dc=github,dc=com - -dn: cn=enterprise-devs,ou=groups,dc=github,dc=com -cn: enterprise-devs -objectClass: groupOfNames -member: uid=benburkert,ou=users,dc=github,dc=com - -dn: cn=enterprise-ops,ou=groups,dc=github,dc=com -cn: enterprise-ops -objectClass: groupOfNames -member: uid=sbryant,ou=users,dc=github,dc=com -member: cn=spaniards,ou=groups,dc=github,dc=com - -dn: cn=spaniards,ou=groups,dc=github,dc=com -cn: spaniards -objectClass: groupOfNames -member: uid=calavera,ou=users,dc=github,dc=com -member: uid=rubiojr,ou=users,dc=github,dc=com - -dn: cn=group1,ou=groups,dc=github,dc=com -cn: group1 -objectClass: groupOfNames -member: uid=user1,ou=users,dc=github,dc=com -member: cn=group1.1,ou=groups,dc=github,dc=com - -dn: cn=group1.1,ou=groups,dc=github,dc=com -cn: group1.1 -objectClass: groupOfNames -member: uid=user1.1,ou=users,dc=github,dc=com -member: cn=group1.1.1,ou=groups,dc=github,dc=com - -dn: cn=group1.1.1,ou=groups,dc=github,dc=com -cn: group1.1.1 -objectClass: groupOfNames -member: uid=user1.1.1,ou=users,dc=github,dc=com -member: cn=group1.1.1.1,ou=groups,dc=github,dc=com - -dn: cn=group1.1.1.1,ou=groups,dc=github,dc=com -cn: group1.1.1.1 -objectClass: groupOfNames -member: uid=user1.1.1.1,ou=users,dc=github,dc=com - -# Users - -dn: ou=users,dc=github,dc=com -objectclass: organizationalUnit -ou: users - -dn: uid=calavera,ou=users,dc=github,dc=com -cn: David Calavera -cn: David -sn: Calavera -uid: calavera -userPassword: passworD1 -mail: calavera@github.com -objectClass: inetOrgPerson - -dn: uid=benburkert,ou=users,dc=github,dc=com -cn: benburkert -sn: benburkert -uid: benburkert -userPassword: passworD1 -mail: benburkert@github.com -objectClass: inetOrgPerson - -dn: uid=sbryant,ou=users,dc=github,dc=com -cn: sbryant -sn: sbryant -uid: sbryant -userPassword: passworD1 -mail: sbryant@github.com -objectClass: inetOrgPerson - -dn: uid=rubiojr,ou=users,dc=github,dc=com -cn: rubiojr -sn: rubiojr -uid: rubiojr -userPassword: passworD1 -mail: rubiojr@github.com -objectClass: inetOrgPerson - -dn: uid=mtodd,ou=users,dc=github,dc=com -cn: mtodd -sn: mtodd -uid: mtodd -userPassword: passworD1 -mail: mtodd@github.com -objectClass: inetOrgPerson - -dn: uid=user1,ou=users,dc=github,dc=com -uid: user1 -sn: user1 -cn: user1 -userPassword: passworD1 -mail: user1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1,ou=users,dc=github,dc=com -uid: user1.1 -sn: user1.1 -cn: user1.1 -userPassword: passworD1 -mail: user1.1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1.1,ou=users,dc=github,dc=com -uid: user1.1.1 -sn: user1.1.1 -cn: user1.1.1 -userPassword: passworD1 -mail: user1.1.1@github.com -objectClass: inetOrgPerson - -dn: uid=user1.1.1.1,ou=users,dc=github,dc=com -uid: user1.1.1.1 -sn: user1.1.1.1 -cn: user1.1.1.1 -userPassword: passworD1 -mail: user1.1.1.1@github.com -objectClass: inetOrgPerson diff --git a/test/fixtures/openldap/seed.ldif b/test/fixtures/openldap/seed.ldif deleted file mode 100644 index 2a0765f..0000000 --- a/test/fixtures/openldap/seed.ldif +++ /dev/null @@ -1,316 +0,0 @@ -dn: ou=People,dc=github,dc=com -objectClass: top -objectClass: organizationalUnit -ou: People - -dn: ou=Groups,dc=github,dc=com -objectClass: top -objectClass: organizationalUnit -ou: Groups - -dn: uid=user1,ou=People,dc=github,dc=com -uid: user1 -cn: user1 -sn: user1 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user1@github.com - -dn: uid=user2,ou=People,dc=github,dc=com -uid: user2 -cn: user2 -sn: user2 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user2@github.com - -dn: uid=user3,ou=People,dc=github,dc=com -uid: user3 -cn: user3 -sn: user3 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user3@github.com - -dn: uid=user4,ou=People,dc=github,dc=com -uid: user4 -cn: user4 -sn: user4 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user4@github.com - -dn: uid=user5,ou=People,dc=github,dc=com -uid: user5 -cn: user5 -sn: user5 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user5@github.com - -dn: uid=user6,ou=People,dc=github,dc=com -uid: user6 -cn: user6 -sn: user6 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user6@github.com - -dn: uid=user7,ou=People,dc=github,dc=com -uid: user7 -cn: user7 -sn: user7 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user7@github.com - -dn: uid=user8,ou=People,dc=github,dc=com -uid: user8 -cn: user8 -sn: user8 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user8@github.com - -dn: uid=user9,ou=People,dc=github,dc=com -uid: user9 -cn: user9 -sn: user9 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user9@github.com - -dn: uid=user10,ou=People,dc=github,dc=com -uid: user10 -cn: user10 -sn: user10 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: user10@github.com - -dn: uid=emailless-user1,ou=People,dc=github,dc=com -uid: emailless-user1 -cn: emailless-user1 -sn: emailless-user1 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 - -dn: uid=admin1,ou=People,dc=github,dc=com -uid: admin1 -cn: admin1 -sn: admin1 -objectClass: top -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -userPassword: passworD1 -mail: admin1@github.com - -dn: cn=ghe-users,ou=Groups,dc=github,dc=com -cn: ghe-users -objectClass: groupOfNames -member: uid=user1,ou=People,dc=github,dc=com -member: uid=emailless-user1,ou=People,dc=github,dc=com - -dn: cn=all-users,ou=Groups,dc=github,dc=com -cn: all-users -objectClass: groupOfNames -member: cn=ghe-users,ou=Groups,dc=github,dc=com -member: uid=user1,ou=People,dc=github,dc=com -member: uid=user2,ou=People,dc=github,dc=com -member: uid=user3,ou=People,dc=github,dc=com -member: uid=user4,ou=People,dc=github,dc=com -member: uid=user5,ou=People,dc=github,dc=com -member: uid=user6,ou=People,dc=github,dc=com -member: uid=user7,ou=People,dc=github,dc=com -member: uid=user8,ou=People,dc=github,dc=com -member: uid=user9,ou=People,dc=github,dc=com -member: uid=user10,ou=People,dc=github,dc=com -member: uid=emailless-user1,ou=People,dc=github,dc=com - -dn: cn=ghe-admins,ou=Groups,dc=github,dc=com -cn: ghe-admins -objectClass: groupOfNames -member: uid=admin1,ou=People,dc=github,dc=com - -dn: cn=all-admins,ou=Groups,dc=github,dc=com -cn: all-admins -objectClass: groupOfNames -member: cn=ghe-admins,ou=Groups,dc=github,dc=com -member: uid=admin1,ou=People,dc=github,dc=com - -dn: cn=n-member-group10,ou=Groups,dc=github,dc=com -cn: n-member-group10 -objectClass: groupOfNames -member: uid=user1,ou=People,dc=github,dc=com -member: uid=user2,ou=People,dc=github,dc=com -member: uid=user3,ou=People,dc=github,dc=com -member: uid=user4,ou=People,dc=github,dc=com -member: uid=user5,ou=People,dc=github,dc=com -member: uid=user6,ou=People,dc=github,dc=com -member: uid=user7,ou=People,dc=github,dc=com -member: uid=user8,ou=People,dc=github,dc=com -member: uid=user9,ou=People,dc=github,dc=com -member: uid=user10,ou=People,dc=github,dc=com - -dn: cn=nested-group1,ou=Groups,dc=github,dc=com -cn: nested-group1 -objectClass: groupOfNames -member: uid=user1,ou=People,dc=github,dc=com -member: uid=user2,ou=People,dc=github,dc=com -member: uid=user3,ou=People,dc=github,dc=com -member: uid=user4,ou=People,dc=github,dc=com -member: uid=user5,ou=People,dc=github,dc=com -member: uid=user6,ou=People,dc=github,dc=com -member: uid=user7,ou=People,dc=github,dc=com -member: uid=user8,ou=People,dc=github,dc=com -member: uid=user9,ou=People,dc=github,dc=com -member: uid=user10,ou=People,dc=github,dc=com - -dn: cn=nested-groups,ou=Groups,dc=github,dc=com -cn: nested-groups -objectClass: groupOfNames -member: cn=nested-group1,ou=Groups,dc=github,dc=com - -dn: cn=n-member-nested-group1,ou=Groups,dc=github,dc=com -cn: n-member-nested-group1 -objectClass: groupOfNames -member: cn=nested-group1,ou=Groups,dc=github,dc=com - -dn: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com -cn: deeply-nested-group0.0.0 -objectClass: groupOfNames -member: uid=user1,ou=People,dc=github,dc=com -member: uid=user2,ou=People,dc=github,dc=com -member: uid=user3,ou=People,dc=github,dc=com -member: uid=user4,ou=People,dc=github,dc=com -member: uid=user5,ou=People,dc=github,dc=com - -dn: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com -cn: deeply-nested-group0.0.1 -objectClass: groupOfNames -member: uid=user6,ou=People,dc=github,dc=com -member: uid=user7,ou=People,dc=github,dc=com -member: uid=user8,ou=People,dc=github,dc=com -member: uid=user9,ou=People,dc=github,dc=com -member: uid=user10,ou=People,dc=github,dc=com - -dn: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com -cn: deeply-nested-group0.0 -objectClass: groupOfNames -member: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com -member: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com - -dn: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com -cn: deeply-nested-group0 -objectClass: groupOfNames -member: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com - -dn: cn=deeply-nested-groups,ou=Groups,dc=github,dc=com -cn: deeply-nested-groups -objectClass: groupOfNames -member: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group1 -objectClass: groupOfNames -member: cn=nested-group1,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group2 -objectClass: groupOfNames -member: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group3 -objectClass: groupOfNames -member: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group4 -objectClass: groupOfNames -member: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group5 -objectClass: groupOfNames -member: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group6 -objectClass: groupOfNames -member: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group7 -objectClass: groupOfNames -member: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group8 -objectClass: groupOfNames -member: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com - -dn: cn=n-depth-nested-group9,ou=Groups,dc=github,dc=com -cn: n-depth-nested-group9 -objectClass: groupOfNames -member: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com - -dn: cn=head-group,ou=Groups,dc=github,dc=com -cn: head-group -objectClass: groupOfNames -member: cn=tail-group,ou=Groups,dc=github,dc=com -member: uid=user1,ou=People,dc=github,dc=com -member: uid=user2,ou=People,dc=github,dc=com -member: uid=user3,ou=People,dc=github,dc=com -member: uid=user4,ou=People,dc=github,dc=com -member: uid=user5,ou=People,dc=github,dc=com - -dn: cn=tail-group,ou=Groups,dc=github,dc=com -cn: tail-group -objectClass: groupOfNames -member: cn=head-group,ou=Groups,dc=github,dc=com -member: uid=user6,ou=People,dc=github,dc=com -member: uid=user7,ou=People,dc=github,dc=com -member: uid=user8,ou=People,dc=github,dc=com -member: uid=user9,ou=People,dc=github,dc=com -member: uid=user10,ou=People,dc=github,dc=com - -dn: cn=recursively-nested-groups,ou=Groups,dc=github,dc=com -cn: recursively-nested-groups -objectClass: groupOfNames -member: cn=head-group,ou=Groups,dc=github,dc=com -member: cn=tail-group,ou=Groups,dc=github,dc=com From 9d154e8d43b213e62dc4546fff9ac936680af3c0 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 15 Oct 2014 15:53:47 -0700 Subject: [PATCH 063/307] Require membership validator strategies Removed deprecated autoload. h/t @jch --- lib/github/ldap/membership_validators.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index 9e3b49d..c355589 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -1,3 +1,7 @@ +require 'github/ldap/membership_validators/base' +require 'github/ldap/membership_validators/classic' +require 'github/ldap/membership_validators/recursive' + module GitHub class Ldap # Provides various strategies for validating membership. @@ -8,10 +12,6 @@ class Ldap # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, groups) # validator.perform(entry) #=> true # - module MembershipValidators - autoload :Base, 'github/ldap/membership_validators/base' - autoload :Classic, 'github/ldap/membership_validators/classic' - autoload :Recursive, 'github/ldap/membership_validators/recursive' - end + module MembershipValidators; end end end From fd2bb1bfbcd3d27ad06bdc977dd721efb0a40ad0 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 16 Oct 2014 12:17:16 -0700 Subject: [PATCH 064/307] Add ActiveDirectory membership validator --- lib/github/ldap/membership_validators.rb | 1 + .../membership_validators/active_directory.rb | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 lib/github/ldap/membership_validators/active_directory.rb diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index c355589..ec3f7b9 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -1,6 +1,7 @@ require 'github/ldap/membership_validators/base' require 'github/ldap/membership_validators/classic' require 'github/ldap/membership_validators/recursive' +require 'github/ldap/membership_validators/active_directory' module GitHub class Ldap diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb new file mode 100644 index 0000000..baf8ba7 --- /dev/null +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -0,0 +1,56 @@ +module GitHub + class Ldap + module MembershipValidators + ATTRS = %w(dn) + OID = "1.2.840.113556.1.4.1941" + + # Validates membership using the ActiveDirectory "in chain" matching rule. + # + # The 1.2.840.113556.1.4.1941 matching rule (LDAP_MATCHING_RULE_IN_CHAIN) + # "walks the chain of ancestry in objects all the way to the root until + # it finds a match". + # Source: http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx + # + # This means we have an efficient method of searching membership even in + # nested groups, performed on the server side. + class ActiveDirectory < Base + def perform(entry, depth = DEFAULT_MAX_DEPTH) + domains.each do |domain| + # search for the entry on the condition that the entry is a member + # of one of the groups or their subgroups. + # + # Sets the entry to the base and scopes the search to the base, + # according to the source documentation, found here: + # http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx + matched = domain.search \ + filter: membership_in_chain_filter(entry), + base: entry.dn, + scope: Net::LDAP::SearchScope_BaseObject, + attributes: ATTRS + + return true unless matched.empty? + end + + false + end + + # Internal: Constructs a membership filter using the "in chain" + # extended matching rule afforded by ActiveDirectory. + # + # Returns a Net::LDAP::Filter object. + def membership_in_chain_filter(entry) + group_dns.map do |dn| + Net::LDAP::Filter.ex("memberOf:#{OID}", dn) + end.reduce(:|) + end + + # Internal: the group DNs to check against. + # + # Returns an Array of String DNs. + def group_dns + @group_dns ||= groups.map(&:dn) + end + end + end + end +end From 9e008b796439ba596b20ab512c584d4c3421e4c9 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 16 Oct 2014 14:51:05 -0700 Subject: [PATCH 065/307] No depth argument for this strategy --- lib/github/ldap/membership_validators/active_directory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index baf8ba7..7113256 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -14,7 +14,7 @@ module MembershipValidators # This means we have an efficient method of searching membership even in # nested groups, performed on the server side. class ActiveDirectory < Base - def perform(entry, depth = DEFAULT_MAX_DEPTH) + def perform(entry) domains.each do |domain| # search for the entry on the condition that the entry is a member # of one of the groups or their subgroups. From 22333a0c8fb0970783113ef1f602b6b7acf194fc Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 16 Oct 2014 15:00:38 -0700 Subject: [PATCH 066/307] Search directly against directory, simplify match check Using GitHub::Ldap::Domain#search forces the base to the domain base (unsurprising yet surprising). --- .../membership_validators/active_directory.rb | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 7113256..b06f886 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -15,23 +15,20 @@ module MembershipValidators # nested groups, performed on the server side. class ActiveDirectory < Base def perform(entry) - domains.each do |domain| - # search for the entry on the condition that the entry is a member - # of one of the groups or their subgroups. - # - # Sets the entry to the base and scopes the search to the base, - # according to the source documentation, found here: - # http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx - matched = domain.search \ - filter: membership_in_chain_filter(entry), - base: entry.dn, - scope: Net::LDAP::SearchScope_BaseObject, - attributes: ATTRS + # search for the entry on the condition that the entry is a member + # of one of the groups or their subgroups. + # + # Sets the entry to the base and scopes the search to the base, + # according to the source documentation, found here: + # http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx + matched = ldap.search \ + filter: membership_in_chain_filter(entry), + base: entry.dn, + scope: Net::LDAP::SearchScope_BaseObject, + attributes: ATTRS - return true unless matched.empty? - end - - false + # membership validated if entry was matched and returned as a result + matched.map(&:dn).include?(entry.dn) end # Internal: Constructs a membership filter using the "in chain" From f008a495cb2043a5daccd6f005f041d43841ece4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 16 Oct 2014 15:31:14 -0700 Subject: [PATCH 067/307] Add simple, stubbed ActiveDirectory membership validator tests --- .../active_directory_test.rb | 77 +++++++++++++++++++ test/test_helper.rb | 1 + 2 files changed, 78 insertions(+) create mode 100644 test/membership_validators/active_directory_test.rb diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb new file mode 100644 index 0000000..feade15 --- /dev/null +++ b/test/membership_validators/active_directory_test.rb @@ -0,0 +1,77 @@ +require_relative '../test_helper' + +# NOTE: Since this strategy is targeted at ActiveDirectory and we don't have +# AD setup in CI, we stub out actual queries and test against what AD *would* +# respond with. + +class GitHubLdapActiveDirectoryMembershipValidatorsTest < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @validator = GitHub::Ldap::MembershipValidators::ActiveDirectory + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + # Stub search to return the given results. + def stub_search(result) + @ldap.stub :search, result do + yield + end + end + + def test_validates_user_in_group + stub_search [@entry] do + validator = make_validator(%w(nested-group1)) + assert validator.perform(@entry) + end + end + + def test_validates_user_in_child_group + stub_search [@entry] do + validator = make_validator(%w(n-depth-nested-group1)) + assert validator.perform(@entry) + end + end + + def test_validates_user_in_grandchild_group + stub_search [@entry] do + validator = make_validator(%w(n-depth-nested-group2)) + assert validator.perform(@entry) + end + end + + def test_validates_user_in_great_grandchild_group + stub_search [@entry] do + validator = make_validator(%w(n-depth-nested-group3)) + assert validator.perform(@entry) + end + end + + def test_does_not_validate_user_not_in_group + stub_search [] do + validator = make_validator(%w(ghe-admins)) + refute validator.perform(@entry) + end + end + + def test_does_not_validate_user_not_in_any_group + entry = @domain.user?('groupless-user1') + + stub_search [] do + validator = make_validator(%w(all-users)) + refute validator.perform(entry) + end + end + + def test_validates_user_in_posix_group + stub_search [@entry] do + validator = make_validator(%w(posix-group1)) + assert validator.perform(@entry) + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index feee10f..8e4b6e9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -10,6 +10,7 @@ require 'github/ldap' require 'github/ldap/server' +require 'minitest/mock' require 'minitest/autorun' if ENV.fetch('TESTENV', "apacheds") == "apacheds" From e63e2afb24dc0989f481ffed18027d51411149ba Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 16 Oct 2014 15:41:12 -0700 Subject: [PATCH 068/307] Short circuit membership valiation without groups to check Default to true since no groups to validate against translates to existing handling: if no groups to check against, then they are valid members. --- lib/github/ldap/membership_validators/active_directory.rb | 3 +++ lib/github/ldap/membership_validators/classic.rb | 1 + lib/github/ldap/membership_validators/recursive.rb | 3 +++ 3 files changed, 7 insertions(+) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index b06f886..0c531c4 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -15,6 +15,9 @@ module MembershipValidators # nested groups, performed on the server side. class ActiveDirectory < Base def perform(entry) + # short circuit validation if there are no groups to check against + return true if groups.empty? + # search for the entry on the condition that the entry is a member # of one of the groups or their subgroups. # diff --git a/lib/github/ldap/membership_validators/classic.rb b/lib/github/ldap/membership_validators/classic.rb index f1cda3e..7fafb3d 100644 --- a/lib/github/ldap/membership_validators/classic.rb +++ b/lib/github/ldap/membership_validators/classic.rb @@ -7,6 +7,7 @@ module MembershipValidators # it consistently with the new approach. class Classic < Base def perform(entry) + # short circuit validation if there are no groups to check against return true if groups.empty? domains.each do |domain| diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 7d4936c..8c40aeb 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -22,6 +22,9 @@ class Recursive < Base ATTRS = %w(dn cn) def perform(entry, depth = DEFAULT_MAX_DEPTH) + # short circuit validation if there are no groups to check against + return true if groups.empty? + domains.each do |domain| # find groups entry is an immediate member of membership = domain.search(filter: member_filter(entry), attributes: ATTRS) From 06e809091edf263b991765f1f494149e85c9312d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 10:49:33 -0700 Subject: [PATCH 069/307] Stub search results directly h/t @jch --- .../active_directory_test.rb | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index feade15..a1c4503 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -17,43 +17,36 @@ def make_validator(groups) @validator.new(@ldap, groups) end - # Stub search to return the given results. - def stub_search(result) - @ldap.stub :search, result do - yield - end - end - def test_validates_user_in_group - stub_search [@entry] do + @ldap.stub :search, [@entry] do validator = make_validator(%w(nested-group1)) assert validator.perform(@entry) end end def test_validates_user_in_child_group - stub_search [@entry] do + @ldap.stub :search, [@entry] do validator = make_validator(%w(n-depth-nested-group1)) assert validator.perform(@entry) end end def test_validates_user_in_grandchild_group - stub_search [@entry] do + @ldap.stub :search, [@entry] do validator = make_validator(%w(n-depth-nested-group2)) assert validator.perform(@entry) end end def test_validates_user_in_great_grandchild_group - stub_search [@entry] do + @ldap.stub :search, [@entry] do validator = make_validator(%w(n-depth-nested-group3)) assert validator.perform(@entry) end end def test_does_not_validate_user_not_in_group - stub_search [] do + @ldap.stub :search, [] do validator = make_validator(%w(ghe-admins)) refute validator.perform(@entry) end @@ -62,14 +55,14 @@ def test_does_not_validate_user_not_in_group def test_does_not_validate_user_not_in_any_group entry = @domain.user?('groupless-user1') - stub_search [] do + @ldap.stub :search, [] do validator = make_validator(%w(all-users)) refute validator.perform(entry) end end def test_validates_user_in_posix_group - stub_search [@entry] do + @ldap.stub :search, [@entry] do validator = make_validator(%w(posix-group1)) assert validator.perform(@entry) end From 1c37c2291a5da285a036cd3a92963c7238021b71 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 10:50:04 -0700 Subject: [PATCH 070/307] posixGroups are not a thing in ActiveDirectory --- test/membership_validators/active_directory_test.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index a1c4503..4c9f652 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -60,11 +60,4 @@ def test_does_not_validate_user_not_in_any_group refute validator.perform(entry) end end - - def test_validates_user_in_posix_group - @ldap.stub :search, [@entry] do - validator = make_validator(%w(posix-group1)) - assert validator.perform(@entry) - end - end end From b971627acc0a26c36a4c55b82c660172749b2031 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 12:07:34 -0700 Subject: [PATCH 071/307] Include all tests in run --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 940d70f..5b19c5a 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require 'rake/testtask' Rake::TestTask.new do |t| t.libs << "test" - t.pattern = "test/*_test.rb" + t.pattern = "test/**/*_test.rb" end task :default => :test From d32bcfbb0dbee7f3ad40562fa02fb587cfa7f7c6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 12:19:21 -0700 Subject: [PATCH 072/307] Move setup out of stub block The stub was affecting the search for the groups, tada. --- .../active_directory_test.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index 4c9f652..b44d9b2 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -18,45 +18,50 @@ def make_validator(groups) end def test_validates_user_in_group + validator = make_validator(%w(nested-group1)) + @ldap.stub :search, [@entry] do - validator = make_validator(%w(nested-group1)) assert validator.perform(@entry) end end def test_validates_user_in_child_group + validator = make_validator(%w(n-depth-nested-group1)) + @ldap.stub :search, [@entry] do - validator = make_validator(%w(n-depth-nested-group1)) assert validator.perform(@entry) end end def test_validates_user_in_grandchild_group + validator = make_validator(%w(n-depth-nested-group2)) + @ldap.stub :search, [@entry] do - validator = make_validator(%w(n-depth-nested-group2)) assert validator.perform(@entry) end end def test_validates_user_in_great_grandchild_group + validator = make_validator(%w(n-depth-nested-group3)) + @ldap.stub :search, [@entry] do - validator = make_validator(%w(n-depth-nested-group3)) assert validator.perform(@entry) end end def test_does_not_validate_user_not_in_group + validator = make_validator(%w(ghe-admins)) + @ldap.stub :search, [] do - validator = make_validator(%w(ghe-admins)) refute validator.perform(@entry) end end def test_does_not_validate_user_not_in_any_group entry = @domain.user?('groupless-user1') + validator = make_validator(%w(all-users)) @ldap.stub :search, [] do - validator = make_validator(%w(all-users)) refute validator.perform(entry) end end From 91adedd7e480dfc7e30ff6777c513e2c246f9a89 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 15:00:48 -0700 Subject: [PATCH 073/307] Allow configurable OpenLDAP host (for local runs) --- test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 8e4b6e9..7780371 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -71,7 +71,7 @@ def options instrumentation_service: @service when "openldap" { - host: 'localhost', + host: ENV.fetch("OPENLDAP_HOST", "localhost"), port: 389, admin_user: 'uid=admin,dc=github,dc=com', admin_password: 'passworD1', From c325873f51c0f3ee9f8c6063676e9ce418679e37 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 15:01:23 -0700 Subject: [PATCH 074/307] Add Vagrantfile for running OpenLDAP tests --- test/support/vm/openldap/.gitignore | 1 + test/support/vm/openldap/Vagrantfile | 33 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/support/vm/openldap/.gitignore create mode 100644 test/support/vm/openldap/Vagrantfile diff --git a/test/support/vm/openldap/.gitignore b/test/support/vm/openldap/.gitignore new file mode 100644 index 0000000..dace708 --- /dev/null +++ b/test/support/vm/openldap/.gitignore @@ -0,0 +1 @@ +/.vagrant diff --git a/test/support/vm/openldap/Vagrantfile b/test/support/vm/openldap/Vagrantfile new file mode 100644 index 0000000..3bb3981 --- /dev/null +++ b/test/support/vm/openldap/Vagrantfile @@ -0,0 +1,33 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.hostname = "openldap.github.org" + + config.vm.box = "hashicorp/precise64" + + config.vm.network "private_network", type: :dhcp + + config.ssh.forward_agent = true + + config.vm.provision "shell", inline: "apt-get update; exec env /vagrant_data/script/install-openldap" + + config.vm.synced_folder "../../../..", "/vagrant_data" + + config.vm.provider "vmware_fusion" do |vb, override| + override.vm.box = "hashicorp/precise64" + vb.memory = 4596 + vb.vmx["displayname"] = "integration tests vm" + vb.vmx["numvcpus"] = "2" + end + + config.vm.provider "virtualbox" do |vb, override| + vb.memory = 4096 + vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"] + vb.customize ["modifyvm", :id, "--chipset", "ich9"] + vb.customize ["modifyvm", :id, "--vram", "16"] + end +end From bea78ba08461ffe26ebf7e7a6587c85f767954a3 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 17 Oct 2014 15:03:57 -0700 Subject: [PATCH 075/307] Add VM README --- test/support/vm/openldap/README.md | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/support/vm/openldap/README.md diff --git a/test/support/vm/openldap/README.md b/test/support/vm/openldap/README.md new file mode 100644 index 0000000..67a4ded --- /dev/null +++ b/test/support/vm/openldap/README.md @@ -0,0 +1,32 @@ +# Local OpenLDAP Integration Testing + +Set up a [Vagrant](http://www.vagrantup.com/) VM to run tests against OpenLDAP locally. + +To run tests against OpenLDAP (instead of ApacheDS) locally: + +``` bash +# start VM (from the correct directory) +$ cd test/support/vm/openldap/ +$ vagrant up + +# get the IP address of the VM +$ ip=$(vagrant ssh -- "ifconfig eth1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1") + +# change back to root project directory +$ cd ../../../.. + +# run all tests against OpenLDAP +$ time TESTENV=openldap OPENLDAP_HOST=$ip bundle exec rake + +# run a specific test file against OpenLDAP +$ time TESTENV=openldap OPENLDAP_HOST=$ip bundle exec ruby test/membership_validators/recursive_test.rb + +# run OpenLDAP tests by default +$ export TESTENV=openldap +$ export TESTENV=$ip + +# now run tests without having to set ENV variables +$ time bundle exec rake +``` + +You may need to `gem install vagrant` first in order to provision the VM. From 228044b13cbe14fd1e07641ce8c14a038071aec9 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Thu, 23 Oct 2014 06:28:24 -0700 Subject: [PATCH 076/307] document all initialize options --- README.md | 2 ++ lib/github/ldap.rb | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/README.md b/README.md index eb86a8b..2b45e82 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Initialize a new adapter using those required options: ldap = GitHub::Ldap.new options ``` +See GitHub::Ldap#initialize for additional options. + ### Querying Searches are performed against an individual domain base, so the first step is to get a new `GitHub::Ldap::Domain` object for the connection: diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 1484bec..2edf08a 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -36,6 +36,28 @@ class Ldap attr_reader :uid, :search_domains, :virtual_attributes, :instrumentation_service + # Build a new GitHub::Ldap instance + # + # ## Connection + # + # host: required string ldap server host address + # port: required string or number ldap server port + # encryption: optional string. `ssl` or `tls`. nil by default + # admin_user: optional string ldap administrator user dn for authentication + # admin_password: optional string ldap administrator user password + # + # ## Behavior + # + # uid: optional field name used to authenticate users. Defaults to `sAMAccountName` (what ActiveDirectory uses) + # virtual_attributes: optional. boolean true to use server's virtual attributes. Hash to specify custom mapping. Default false. + # recursive_group_search_fallback: optional boolean whether membership checks should recurse into nested groups when virtual attributes aren't enabled. Default false. + # posix_support: optional boolean `posixGroup` support. Default true. + # search_domains: optional array of string bases to search through + # + # ## Diagnostics + # + # instrumentation_service: optional ActiveSupport::Notifications compatible object + # def initialize(options = {}) @uid = options[:uid] || "sAMAccountName" From cd3934ec79f2d13d76c095ec769045e5e74687f0 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 14:11:42 -0700 Subject: [PATCH 077/307] Add membership_validator config option, default to detect --- lib/github/ldap.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 2edf08a..19d241a 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -34,6 +34,7 @@ class Ldap def_delegator :@connection, :open attr_reader :uid, :search_domains, :virtual_attributes, + :membership_validator, :instrumentation_service # Build a new GitHub::Ldap instance @@ -87,6 +88,9 @@ def initialize(options = {}) # when a base is not explicitly provided. @search_domains = Array(options[:search_domains]) + # configure which strategy should be used to validate user membership + configure_membership_validation_strategy(options[:membership_validator]) + # enables instrumenting queries @instrumentation_service = options[:instrumentation_service] end @@ -214,5 +218,24 @@ def configure_virtual_attributes(attributes) VirtualAttributes.new(false) end end + + # Internal: Configure the membership validation strategy. + # + # Used by GitHub::Ldap::MembershipValidators::Detect to force a specific + # strategy (instead of detecting host capabilities and deciding at runtime). + # + # If `strategy` is not provided, or doesn't match a known strategy, + # defaults to `:detect`. Otherwise the configured strategy is selected. + # + # Returns the selected membership validator strategy Symbol. + def configure_membership_validation_strategy(strategy = nil) + @membership_validator = + case strategy.to_s + when "classic", "recursive", "active_directory" + strategy.to_sym + else + :detect + end + end end end From 22d7995f40ffdba9dded76fb174b2ab8f7878b3d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 14:13:03 -0700 Subject: [PATCH 078/307] Add Detect membership validation strategy Detects ActiveDirectory capabilities, defaults to Recursive. --- lib/github/ldap/membership_validators.rb | 1 + .../ldap/membership_validators/detect.rb | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 lib/github/ldap/membership_validators/detect.rb diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index ec3f7b9..8bba69f 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -1,4 +1,5 @@ require 'github/ldap/membership_validators/base' +require 'github/ldap/membership_validators/detect' require 'github/ldap/membership_validators/classic' require 'github/ldap/membership_validators/recursive' require 'github/ldap/membership_validators/active_directory' diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb new file mode 100644 index 0000000..13d202b --- /dev/null +++ b/lib/github/ldap/membership_validators/detect.rb @@ -0,0 +1,73 @@ +module GitHub + class Ldap + module MembershipValidators + # Detects the LDAP host's capabilities and determines the appropriate + # membership validation strategy at runtime. + class Detect < Base + # Internal: Mapping of strategy name to class. + STRATEGIES = { + :classic => GitHub::Ldap::MembershipValidators::Classic, + :recursive => GitHub::Ldap::MembershipValidators::Recursive, + :active_directory => GitHub::Ldap::MembershipValidators::ActiveDirectory + } + + # Internal: The capability required to use the ActiveDirectory strategy. + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + ACTIVE_DIRECTORY_V51_OID = "1.2.840.113556.1.4.1670".freeze + + def perform(entry) + # short circuit validation if there are no groups to check against + return true if groups.empty? + + strategy.perform(entry) + end + + # Internal: Returns the membership validation strategy object. + def strategy + @strategy ||= begin + strategy = detect_strategy + strategy.new(ldap, groups) + end + end + + # Internal: Detects LDAP host's capabilities and chooses the best + # strategy for the host. + # + # If the strategy has been + # + # Returns the strategy class. + def detect_strategy + return STRATEGIES[strategy_config] if STRATEGIES.key?(strategy_config) + + if active_directory_capability? + :active_directory + else + :recursive + end + end + + # Internal: Returns the configured membership validator strategy Symbol. + def strategy_config + ldap.membership_validator + end + + # Internal: Detect it the LDAP host is an ActiveDirectory server. + # + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + # + # Returns true if the host is an ActiveDirectory server, false otherwise. + def active_directory_capability? + capabilities[:supportcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) + end + + # Internal: Searches the LDAP host's Root DSE for server capabilities. + # + # Returns the Net::LDAP::Entry object containing the Root DSE + # results describing the server capabilities. + def capabilities + @capabilities ||= ldap.search_root_dse + end + end + end + end +end From 838c8b1c8d8c3c85c291929a7846c92a0e83f893 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 14:15:18 -0700 Subject: [PATCH 079/307] Move strategies map to GitHub::Ldap::MembershipValidators --- lib/github/ldap/membership_validators.rb | 9 ++++++++- lib/github/ldap/membership_validators/detect.rb | 14 ++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index 8bba69f..1135a2d 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -14,6 +14,13 @@ class Ldap # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, groups) # validator.perform(entry) #=> true # - module MembershipValidators; end + module MembershipValidators + # Internal: Mapping of strategy name to class. + STRATEGIES = { + :classic => GitHub::Ldap::MembershipValidators::Classic, + :recursive => GitHub::Ldap::MembershipValidators::Recursive, + :active_directory => GitHub::Ldap::MembershipValidators::ActiveDirectory + } + end end end diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 13d202b..2ade0e7 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -4,13 +4,6 @@ module MembershipValidators # Detects the LDAP host's capabilities and determines the appropriate # membership validation strategy at runtime. class Detect < Base - # Internal: Mapping of strategy name to class. - STRATEGIES = { - :classic => GitHub::Ldap::MembershipValidators::Classic, - :recursive => GitHub::Ldap::MembershipValidators::Recursive, - :active_directory => GitHub::Ldap::MembershipValidators::ActiveDirectory - } - # Internal: The capability required to use the ActiveDirectory strategy. # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. ACTIVE_DIRECTORY_V51_OID = "1.2.840.113556.1.4.1670".freeze @@ -37,9 +30,10 @@ def strategy # # Returns the strategy class. def detect_strategy - return STRATEGIES[strategy_config] if STRATEGIES.key?(strategy_config) - - if active_directory_capability? + case + when GitHub::Ldap::MembershipValidators::STRATEGIES.key?(strategy_config) + GitHub::Ldap::MembershipValidators::STRATEGIES[strategy_config] + when active_directory_capability? :active_directory else :recursive From 724177b86660e3206c9942e62f7d2cba0a39c1fb Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 14:23:09 -0700 Subject: [PATCH 080/307] Update required capability OID to match 2008 R2 v61 --- lib/github/ldap/membership_validators/detect.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 2ade0e7..6b50dc7 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -6,7 +6,7 @@ module MembershipValidators class Detect < Base # Internal: The capability required to use the ActiveDirectory strategy. # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - ACTIVE_DIRECTORY_V51_OID = "1.2.840.113556.1.4.1670".freeze + ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze def perform(entry) # short circuit validation if there are no groups to check against @@ -51,7 +51,7 @@ def strategy_config # # Returns true if the host is an ActiveDirectory server, false otherwise. def active_directory_capability? - capabilities[:supportcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) + capabilities[:supportcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) end # Internal: Searches the LDAP host's Root DSE for server capabilities. From fe976b4994615f13bffe11c35df6f94c63253cd1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 15:56:28 -0700 Subject: [PATCH 081/307] Proactively handle capapbility query errors, instrument --- lib/github/ldap/membership_validators/detect.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 6b50dc7..b81da9f 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -59,7 +59,16 @@ def active_directory_capability? # Returns the Net::LDAP::Entry object containing the Root DSE # results describing the server capabilities. def capabilities - @capabilities ||= ldap.search_root_dse + @ldap.instrument "capabilities.github_ldap_membership_validator" do |payload| + @capabilities ||= + begin + ldap.search_root_dse + rescue Net::LDAP::LdapError => error + payload[:error] = error + # stubbed result + Net::LDAP::Entry.new + end + end end end end From 941a577c204aded655ed0dc5ded47b8a8b4919a4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:13:04 -0700 Subject: [PATCH 082/307] Fix detect_strategy doco --- lib/github/ldap/membership_validators/detect.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index b81da9f..04eb885 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -26,7 +26,8 @@ def strategy # Internal: Detects LDAP host's capabilities and chooses the best # strategy for the host. # - # If the strategy has been + # If the strategy has been set explicitly, skips detection and uses the + # configured strategy instead. # # Returns the strategy class. def detect_strategy From 60ee236415a6ff2e952b9c24f1a6a58186117419 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:13:17 -0700 Subject: [PATCH 083/307] Ensure detect_strategy returns the strategy class --- lib/github/ldap/membership_validators/detect.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 04eb885..510ecd2 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -35,9 +35,9 @@ def detect_strategy when GitHub::Ldap::MembershipValidators::STRATEGIES.key?(strategy_config) GitHub::Ldap::MembershipValidators::STRATEGIES[strategy_config] when active_directory_capability? - :active_directory + GitHub::Ldap::MembershipValidators::STRATEGIES[:active_directory] else - :recursive + GitHub::Ldap::MembershipValidators::STRATEGIES[:recursive] end end From 18cdf65e8caaf28695c07497335a918fdb16d4ea Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:14:49 -0700 Subject: [PATCH 084/307] s/if/whether/ --- lib/github/ldap/membership_validators/detect.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 510ecd2..3099d86 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -46,7 +46,7 @@ def strategy_config ldap.membership_validator end - # Internal: Detect it the LDAP host is an ActiveDirectory server. + # Internal: Detect whether the LDAP host is an ActiveDirectory server. # # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. # From 78039780d79a068070bab1fc3836baa73b0026d5 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:36:50 -0700 Subject: [PATCH 085/307] Move capabilities lookup to GitHub::Ldap --- lib/github/ldap.rb | 17 +++++++++++++++++ lib/github/ldap/membership_validators/detect.rb | 17 +++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 19d241a..e556d0b 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -186,6 +186,23 @@ def search(options, &block) end end + # Internal: Searches the host LDAP server's Root DSE for capabilities and + # extensions. + # + # Returns a Net::LDAP::Entry object. + def capabilities + @ldap.instrument "capabilities.github_ldap" do |payload| + @capabilities ||= + begin + @connection.search_root_dse + rescue Net::LDAP::LdapError => error + payload[:error] = error + # stubbed result + Net::LDAP::Entry.new + end + end + end + # Internal - Determine whether to use encryption or not. # # encryption: is the encryption method, either 'ssl', 'tls', 'simple_tls' or 'start_tls'. diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 3099d86..c48aaeb 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -55,21 +55,10 @@ def active_directory_capability? capabilities[:supportcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) end - # Internal: Searches the LDAP host's Root DSE for server capabilities. - # - # Returns the Net::LDAP::Entry object containing the Root DSE - # results describing the server capabilities. + # Internal: Returns the Net::LDAP::Entry object describing the LDAP + # host's capabilities (via the Root DSE). def capabilities - @ldap.instrument "capabilities.github_ldap_membership_validator" do |payload| - @capabilities ||= - begin - ldap.search_root_dse - rescue Net::LDAP::LdapError => error - payload[:error] = error - # stubbed result - Net::LDAP::Entry.new - end - end + ldap.capabilities end end end From 946267668a44960c21df3e1f33dc0b8e8c0b427c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:46:45 -0700 Subject: [PATCH 086/307] Only instrument when we query for capabilities --- lib/github/ldap.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index e556d0b..8a952df 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -191,8 +191,8 @@ def search(options, &block) # # Returns a Net::LDAP::Entry object. def capabilities - @ldap.instrument "capabilities.github_ldap" do |payload| - @capabilities ||= + @capabilities ||= + @ldap.instrument "capabilities.github_ldap" do |payload| begin @connection.search_root_dse rescue Net::LDAP::LdapError => error @@ -200,7 +200,7 @@ def capabilities # stubbed result Net::LDAP::Entry.new end - end + end end # Internal - Determine whether to use encryption or not. From 013bb288683feddb67cff3ff27fe4e30e629edbb Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:48:49 -0700 Subject: [PATCH 087/307] Server returns supportedCapabilities --- lib/github/ldap/membership_validators/detect.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index c48aaeb..6a3f6dc 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -52,7 +52,7 @@ def strategy_config # # Returns true if the host is an ActiveDirectory server, false otherwise. def active_directory_capability? - capabilities[:supportcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) + capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) end # Internal: Returns the Net::LDAP::Entry object describing the LDAP From 963297ff904f1ad4b999724e75c156899df655c6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 24 Oct 2014 16:49:57 -0700 Subject: [PATCH 088/307] Test Detect meta-strategy --- test/membership_validators/detect_test.rb | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/membership_validators/detect_test.rb diff --git a/test/membership_validators/detect_test.rb b/test/membership_validators/detect_test.rb new file mode 100644 index 0000000..8bf522a --- /dev/null +++ b/test/membership_validators/detect_test.rb @@ -0,0 +1,49 @@ +require_relative '../test_helper' + +# NOTE: Since this strategy is targeted at detecting ActiveDirectory +# capabilities, and we don't have AD setup in CI, we stub out actual queries +# and test against what AD *would* respond with. + +class GitHubLdapDetectMembershipValidatorsTest < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @validator = GitHub::Ldap::MembershipValidators::Detect + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_defers_to_configured_strategy + @ldap.configure_membership_validation_strategy(:classic) + validator = make_validator(%w(group)) + + assert_kind_of GitHub::Ldap::MembershipValidators::Classic, validator.strategy + end + + def test_detects_active_directory + caps = Net::LDAP::Entry.new + caps[:supportedcapabilities] = + [GitHub::Ldap::MembershipValidators::Detect::ACTIVE_DIRECTORY_V61_R2_OID] + + validator = make_validator(%w(group)) + @ldap.stub :capabilities, caps do + assert_kind_of GitHub::Ldap::MembershipValidators::ActiveDirectory, + validator.strategy + end + end + + def test_falls_back_to_recursive + caps = Net::LDAP::Entry.new + caps[:supportedcapabilities] = [] + + validator = make_validator(%w(group)) + @ldap.stub :capabilities, caps do + assert_kind_of GitHub::Ldap::MembershipValidators::Recursive, + validator.strategy + end + end +end From f506dc37456d59efd5e8a8a5410adf91bddfb1ec Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 10:05:53 -0700 Subject: [PATCH 089/307] fix typo --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 8a952df..3258ac4 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -192,7 +192,7 @@ def search(options, &block) # Returns a Net::LDAP::Entry object. def capabilities @capabilities ||= - @ldap.instrument "capabilities.github_ldap" do |payload| + instrument "capabilities.github_ldap" do |payload| begin @connection.search_root_dse rescue Net::LDAP::LdapError => error From ba0792cf1f356090af0445b68364758717e5858f Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 10:07:25 -0700 Subject: [PATCH 090/307] add test for validator detection --- test/ldap_test.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 40fcb95..250c6bb 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -72,6 +72,14 @@ def test_instruments_search assert_equal "(uid=user1)", payload[:filter].to_s assert_equal "dc=github,dc=com", payload[:base] end + + def test_membership_validator_default + assert_equal :detect, @ldap.membership_validator + end + + def test_capabilities + assert_kind_of Net::LDAP::Entry, @ldap.capabilities + end end class GitHubLdapTest < GitHub::Ldap::Test From 42e16bf3ee13d88812ad2815a40aa60e95fde47a Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 10:09:53 -0700 Subject: [PATCH 091/307] bump net-ldap to 0.9.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 65c7c70..f06b9d1 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.8.0' + spec.add_dependency 'net-ldap', '~> 0.9.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From 99fc8526b4206628e9b9817927d9db8878ed0033 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 11:49:08 -0700 Subject: [PATCH 092/307] add script/changelog see https://github.com/jch/release-scripts --- script/changelog | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 script/changelog diff --git a/script/changelog b/script/changelog new file mode 100755 index 0000000..6ee2a54 --- /dev/null +++ b/script/changelog @@ -0,0 +1,29 @@ +#!/usr/bin/env sh +# Usage: script/changelog [-r ] [-b ] [-h ] +# +# repo: base string of GitHub repository url. e.g. "user_or_org/repository". Defaults to git remote url. +# base: git ref to compare from. e.g. "v1.3.1". Defaults to latest git tag. +# head: git ref to compare to. Defaults to "HEAD". +# +# Generate a changelog preview from pull requests merged between `base` and +# `head`. +# +set -e + +[ $# -eq 0 ] && set -- --help + +# parse args +repo=$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4-) +base=$(git tag -l | sort -n | tail -n 1) +head="HEAD" +api_url="https://api.github.com" + +echo "# $base..$head" +echo + +# get merged PR's. Better way is to query the API for these, but this is easier +for pr in $(git log --oneline v1.3.6..HEAD | grep "Merge pull request" | awk '{gsub("#",""); print $5}') +do + # frustrated with trying to pull out the right values, fell back to ruby + curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} [##{pr[%q(number)]}](#{pr[%q(html_url)]})"' +done From 0180d118fcc0e375d8fbdd89fdfea5c684f86ecb Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 11:50:03 -0700 Subject: [PATCH 093/307] add a changelog --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d843d2f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# CHANGELOG + +## v1.4.0 + +* Document constructor options [#57](https://github.com/github/github-ldap/pull/57) +* [CI] Add Vagrant box for running tests against OpenLDAP locally [#55](https://github.com/github/github-ldap/pull/55) +* Run all tests, including those in subdirectories [#54](https://github.com/github/github-ldap/pull/54) +* Add ActiveDirectory membership validator [#52](https://github.com/github/github-ldap/pull/52) +* Merge dev-v2 branch into master [#50](https://github.com/github/github-ldap/pull/50) +* Pass through search options for GitHub::Ldap::Domain#user? [#51](https://github.com/github/github-ldap/pull/51) +* Fix membership validation tests [#49](https://github.com/github/github-ldap/pull/49) +* Add CI build for OpenLDAP integration [#48](https://github.com/github/github-ldap/pull/48) +* Membership Validators [#45](https://github.com/github/github-ldap/pull/45) From d06d6779da899de99c384e738922bc53a7031370 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 11:51:19 -0700 Subject: [PATCH 094/307] bump version to 1.4.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 65c7c70..467952f 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.3.6" + spec.version = "1.4.0" spec.authors = ["David Calavera"] spec.email = ["david.calavera@gmail.com"] spec.description = %q{Ldap authentication for humans} From 9beddcf4fabe14a27d8e51ab6d80c8a645916f24 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 11:52:12 -0700 Subject: [PATCH 095/307] add release scripts --- script/package | 7 +++++++ script/release | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100755 script/package create mode 100755 script/release diff --git a/script/package b/script/package new file mode 100755 index 0000000..5851400 --- /dev/null +++ b/script/package @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Usage: script/package +# Updates the gemspec and builds a new gem in the pkg directory. + +mkdir -p pkg +gem build *.gemspec +mv *.gem pkg diff --git a/script/release b/script/release new file mode 100755 index 0000000..6dcd8cb --- /dev/null +++ b/script/release @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Usage: script/release +# Build the package, tag a commit, push it to origin, and then release the +# package publicly. + +set -e + +version="$(script/package | grep Version: | awk '{print $2}')" +[ -n "$version" ] || exit 1 + +echo $version +git commit --allow-empty -a -m "Release $version" +git tag "v$version" +git push origin +git push origin "v$version" +gem push pkg/*-${version}.gem From cfaf1cf13e866a8b23e89cd62585e0fdafa3913e Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 13:25:10 -0700 Subject: [PATCH 096/307] document how to release --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 2b45e82..482f6ea 100644 --- a/README.md +++ b/README.md @@ -130,3 +130,15 @@ end 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request + +## Releasing + +This section is for gem maintainers to cut a new version of the gem. See +[jch/release-scripts](https://github.com/jch/release-scripts) for original +source of release scripts. + +* Create a new branch from `master` named `release-x.y.z`, where `x.y.z` is the version to be released +* Update `github-ldap.gemspec` to x.y.z following [semver](http://semver.org) +* Run `script/changelog` and paste the draft into `CHANGELOG.md`. Edit as needed +* Create pull request to solict feedback +* After merging the pull request, on the master branch, run `script/release` From 2aa8c10eea518d2ffd4d8d917b7ab6c50dfded42 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 15:12:47 -0700 Subject: [PATCH 097/307] document local AD integration testing --- test/support/vm/activedirectory/.gitignore | 1 + test/support/vm/activedirectory/README.md | 23 +++++++++++++++++++ .../support/vm/activedirectory/env.sh.example | 8 +++++++ test/support/vm/activedirectory/reset-env.sh | 6 +++++ 4 files changed, 38 insertions(+) create mode 100644 test/support/vm/activedirectory/.gitignore create mode 100644 test/support/vm/activedirectory/README.md create mode 100644 test/support/vm/activedirectory/env.sh.example create mode 100644 test/support/vm/activedirectory/reset-env.sh diff --git a/test/support/vm/activedirectory/.gitignore b/test/support/vm/activedirectory/.gitignore new file mode 100644 index 0000000..137e678 --- /dev/null +++ b/test/support/vm/activedirectory/.gitignore @@ -0,0 +1 @@ +env.sh diff --git a/test/support/vm/activedirectory/README.md b/test/support/vm/activedirectory/README.md new file mode 100644 index 0000000..7450b49 --- /dev/null +++ b/test/support/vm/activedirectory/README.md @@ -0,0 +1,23 @@ +# Local ActiveDirectory Integration Testing + +Integration tests are not run for ActiveDirectory in continuous integration +because we cannot install a Windows VM on TravisCI. To test ActiveDirectory, +configure a local VM with AD running. + +From the project root, run: + +```sh +$ cp test/support/vm/activedirectory/env.sh{.example,} + +# edit ad-env.sh and fill in with your VM's values, then +$ source test/support/vm/activedirectory/env.sh + +# run all tests against AD +$ time bundle exec rake + +# run a specific test file against AD +$ time bundle exec ruby test/membership_validators/active_directory_test.rb + +# reset environment to test other ldap servers +$ source test/support/vm/activedirectory/reset-env.sh +``` diff --git a/test/support/vm/activedirectory/env.sh.example b/test/support/vm/activedirectory/env.sh.example new file mode 100644 index 0000000..5a17fe8 --- /dev/null +++ b/test/support/vm/activedirectory/env.sh.example @@ -0,0 +1,8 @@ +# Copy this to ad-env.sh, and fill in with your own values + +export TESTENV=activedirectory +export ACTIVEDIRECTORY_HOST=123.123.123.123 +export ACTIVEDIRECTORY_PORT=389 +export ACTIVEDIRECTORY_USER="CN=Administrator,CN=Users,DC=ad,DC=example,DC=com" +export ACTIVEDIRECTORY_PASSWORD='passworD1' +export ACTIVEDIRECTORY_SEARCH_DOMAINS='CN=Users,DC=ad,DC=ghe,DC=local' diff --git a/test/support/vm/activedirectory/reset-env.sh b/test/support/vm/activedirectory/reset-env.sh new file mode 100644 index 0000000..6bfab09 --- /dev/null +++ b/test/support/vm/activedirectory/reset-env.sh @@ -0,0 +1,6 @@ +unset TESTENV +unset ACTIVEDIRECTORY_HOST +unset ACTIVEDIRECTORY_PORT +unset ACTIVEDIRECTORY_USER +unset ACTIVEDIRECTORY_PASSWORD +unset ACTIVEDIRECTORY_SEARCH_DOMAINS From 8ef4551e16a69e0ee4d9e63cb7cb5433455aa26b Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 15:13:13 -0700 Subject: [PATCH 098/307] add activedirectory test options --- test/test_helper.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_helper.rb b/test/test_helper.rb index 7780371..1f828c4 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -79,6 +79,15 @@ def options uid: 'uid', instrumentation_service: @service } + when "activedirectory" + { + host: ENV.fetch("ACTIVEDIRECTORY_HOST"), + port: ENV.fetch("ACTIVEDIRECTORY_PORT", 389), + admin_user: ENV.fetch("ACTIVEDIRECTORY_USER"), + admin_password: ENV.fetch("ACTIVEDIRECTORY_PASSWORD"), + search_domains: ENV.fetch("ACTIVEDIRECTORY_SEARCH_DOMAINS"), + instrumentation_service: @service + } end end end From cf0d90720f67179f8fff0409fc36b2778db3cc64 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 15:13:31 -0700 Subject: [PATCH 099/307] add AD validator integration tests --- .../active_directory_test.rb | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index b44d9b2..0caafe2 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -1,10 +1,11 @@ require_relative '../test_helper' -# NOTE: Since this strategy is targeted at ActiveDirectory and we don't have -# AD setup in CI, we stub out actual queries and test against what AD *would* -# respond with. +class GitHubLdapActiveDirectoryMembershipValidatorsStubbedTest < GitHub::Ldap::Test + # Only run when AD integration tests aren't run + def run(*) + self.class.test_env != "activedirectory" ? super : self + end -class GitHubLdapActiveDirectoryMembershipValidatorsTest < GitHub::Ldap::Test def setup @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") @@ -66,3 +67,60 @@ def test_does_not_validate_user_not_in_any_group end end end + +# See test/support/vm/activedirectory/README.md for details +class GitHubLdapActiveDirectoryMembershipValidatorsIntegrationTest < GitHub::Ldap::Test + # Only run this test suite if ActiveDirectory is configured + def run(*) + self.class.test_env == "activedirectory" ? super : self + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain(options[:search_domains]) + @entry = @domain.user?('user1') + @validator = GitHub::Ldap::MembershipValidators::ActiveDirectory + end + + def make_validator(groups) + groups = @domain.groups(groups) + @validator.new(@ldap, groups) + end + + def test_validates_user_in_group + validator = make_validator(%w(nested-group1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_child_group + validator = make_validator(%w(n-depth-nested-group1)) + assert validator.perform(@entry) + end + + def test_validates_user_in_grandchild_group + validator = make_validator(%w(n-depth-nested-group2)) + assert validator.perform(@entry) + end + + def test_validates_user_in_great_grandchild_group + validator = make_validator(%w(n-depth-nested-group3)) + assert validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_group + validator = make_validator(%w(ghe-admins)) + refute validator.perform(@entry) + end + + def test_does_not_validate_user_not_in_any_group + skip "update AD ldif to have a groupless user" + @entry = @domain.user?('groupless-user1') + validator = make_validator(%w(all-users)) + refute validator.perform(@entry) + end + + def test_validates_user_in_posix_group + validator = make_validator(%w(posix-group1)) + assert validator.perform(@entry) + end +end From 4a7593252198121fe5fdb5a5794260fcd7f14146 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 28 Oct 2014 15:30:28 -0700 Subject: [PATCH 100/307] additional docs on AD membership validator --- lib/github/ldap/membership_validators/detect.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index 6a3f6dc..ba8c4ba 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -2,7 +2,10 @@ module GitHub class Ldap module MembershipValidators # Detects the LDAP host's capabilities and determines the appropriate - # membership validation strategy at runtime. + # membership validation strategy at runtime. Currently detects for + # ActiveDirectory in-chain membership validation. An explicit strategy can + # also be defined via `GitHub::Ldap#membership_validator=`. See also + # `GitHub::Ldap#configure_membership_validation_strategy`. class Detect < Base # Internal: The capability required to use the ActiveDirectory strategy. # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. From 6a37d6871116247430626b751dc9e1a3df2657d7 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Mon, 3 Nov 2014 15:59:26 -0800 Subject: [PATCH 101/307] Release 1.4.0 --- test/support/vm/openldap/Vagrantfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/support/vm/openldap/Vagrantfile b/test/support/vm/openldap/Vagrantfile index 3bb3981..abb44ae 100644 --- a/test/support/vm/openldap/Vagrantfile +++ b/test/support/vm/openldap/Vagrantfile @@ -13,7 +13,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.ssh.forward_agent = true - config.vm.provision "shell", inline: "apt-get update; exec env /vagrant_data/script/install-openldap" + # config.vm.provision "shell", inline: "apt-get update; exec env /vagrant_data/script/install-openldap" + config.vm.provision "shell", inline: 'echo "HIIIIIII"', run: "always" config.vm.synced_folder "../../../..", "/vagrant_data" @@ -30,4 +31,5 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| vb.customize ["modifyvm", :id, "--chipset", "ich9"] vb.customize ["modifyvm", :id, "--vram", "16"] end + end From ef1c4537a475dfaa0af0f3903430ec497062773d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 13:51:36 -0800 Subject: [PATCH 102/307] Test membership validation configuration This replaces the existing tests on the top level MembershipValidators module since those tests were duplicates of more specific tests for each strategy. --- test/membership_validators_test.rb | 49 +++++++++++------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/test/membership_validators_test.rb b/test/membership_validators_test.rb index e1a981f..74e8cf8 100644 --- a/test/membership_validators_test.rb +++ b/test/membership_validators_test.rb @@ -1,46 +1,31 @@ require_relative 'test_helper' -module GitHubLdapMembershipValidatorsTestCases - def make_validator(groups) - groups = @domain.groups(groups) - @validator.new(@ldap, groups) +class GitHubLdapMembershipValidatorsTest < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) end - def test_validates_user_in_group - validator = make_validator(%w(ghe-users)) - assert validator.perform(@entry) + def test_defaults_to_detect_strategy + assert_equal :detect, @ldap.membership_validator end - def test_does_not_validate_user_not_in_group - validator = make_validator(%w(ghe-admins)) - refute validator.perform(@entry) + def test_configured_to_classic_strategy + @ldap.configure_membership_validation_strategy :classic + assert_equal :classic, @ldap.membership_validator end - def test_does_not_validate_user_not_in_any_group - @entry = @domain.user?('groupless-user1') - validator = make_validator(%w(ghe-users ghe-admins)) - refute validator.perform(@entry) + def test_configured_to_recursive_strategy + @ldap.configure_membership_validation_strategy :recursive + assert_equal :recursive, @ldap.membership_validator end -end -class GitHubLdapMembershipValidatorsClassicTest < GitHub::Ldap::Test - include GitHubLdapMembershipValidatorsTestCases - - def setup - @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1') - @validator = GitHub::Ldap::MembershipValidators::Classic + def test_configured_to_active_directory_strategy + @ldap.configure_membership_validation_strategy :active_directory + assert_equal :active_directory, @ldap.membership_validator end -end - -class GitHubLdapMembershipValidatorsRecursiveTest < GitHub::Ldap::Test - include GitHubLdapMembershipValidatorsTestCases - def setup - @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1') - @validator = GitHub::Ldap::MembershipValidators::Recursive + def test_misconfigured_to_unrecognized_strategy_falls_back_to_default + @ldap.configure_membership_validation_strategy :unknown + assert_equal :detect, @ldap.membership_validator end end From 7cd4bc84f305675b27d884e54731df40a2ce77f2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 13:56:37 -0800 Subject: [PATCH 103/307] Move membership validator config tests to GitHub::Ldap test --- test/ldap_test.rb | 20 +++++++++++++++++++ test/membership_validators_test.rb | 31 ------------------------------ 2 files changed, 20 insertions(+), 31 deletions(-) delete mode 100644 test/membership_validators_test.rb diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 250c6bb..6d4d5fb 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -77,6 +77,26 @@ def test_membership_validator_default assert_equal :detect, @ldap.membership_validator end + def test_membership_validator_configured_to_classic_strategy + @ldap.configure_membership_validation_strategy :classic + assert_equal :classic, @ldap.membership_validator + end + + def test_membership_validator_configured_to_recursive_strategy + @ldap.configure_membership_validation_strategy :recursive + assert_equal :recursive, @ldap.membership_validator + end + + def test_membership_validator_configured_to_active_directory_strategy + @ldap.configure_membership_validation_strategy :active_directory + assert_equal :active_directory, @ldap.membership_validator + end + + def test_membership_validator_misconfigured_to_unrecognized_strategy_falls_back_to_default + @ldap.configure_membership_validation_strategy :unknown + assert_equal :detect, @ldap.membership_validator + end + def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end diff --git a/test/membership_validators_test.rb b/test/membership_validators_test.rb deleted file mode 100644 index 74e8cf8..0000000 --- a/test/membership_validators_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -require_relative 'test_helper' - -class GitHubLdapMembershipValidatorsTest < GitHub::Ldap::Test - def setup - @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com")) - end - - def test_defaults_to_detect_strategy - assert_equal :detect, @ldap.membership_validator - end - - def test_configured_to_classic_strategy - @ldap.configure_membership_validation_strategy :classic - assert_equal :classic, @ldap.membership_validator - end - - def test_configured_to_recursive_strategy - @ldap.configure_membership_validation_strategy :recursive - assert_equal :recursive, @ldap.membership_validator - end - - def test_configured_to_active_directory_strategy - @ldap.configure_membership_validation_strategy :active_directory - assert_equal :active_directory, @ldap.membership_validator - end - - def test_misconfigured_to_unrecognized_strategy_falls_back_to_default - @ldap.configure_membership_validation_strategy :unknown - assert_equal :detect, @ldap.membership_validator - end -end From a316402f5326e7d54cebcb29cbba994c15fd8ab1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 14:10:54 -0800 Subject: [PATCH 104/307] Minor tweaks and corrections --- test/support/vm/activedirectory/README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/support/vm/activedirectory/README.md b/test/support/vm/activedirectory/README.md index 7450b49..36155bd 100644 --- a/test/support/vm/activedirectory/README.md +++ b/test/support/vm/activedirectory/README.md @@ -2,14 +2,17 @@ Integration tests are not run for ActiveDirectory in continuous integration because we cannot install a Windows VM on TravisCI. To test ActiveDirectory, -configure a local VM with AD running. +configure a local VM with AD running (this is left as an exercise for the +reader). -From the project root, run: +To run integration tests against the local ActiveDirectory VM, from the project +root run: -```sh +``` bash +# duplicate example env.sh for specific config $ cp test/support/vm/activedirectory/env.sh{.example,} -# edit ad-env.sh and fill in with your VM's values, then +# edit env.sh and fill in with your VM's values, then $ source test/support/vm/activedirectory/env.sh # run all tests against AD @@ -18,6 +21,6 @@ $ time bundle exec rake # run a specific test file against AD $ time bundle exec ruby test/membership_validators/active_directory_test.rb -# reset environment to test other ldap servers +# reset environment to test other LDAP servers $ source test/support/vm/activedirectory/reset-env.sh ``` From ec9021e0956f15447d9aaa2d809df10b7510c152 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 14:26:18 -0800 Subject: [PATCH 105/307] Use INTEGRATION_* instead of flavor-specific ENV vars --- test/support/vm/activedirectory/env.sh.example | 10 +++++----- test/support/vm/activedirectory/reset-env.sh | 10 +++++----- test/support/vm/openldap/README.md | 4 ++-- test/test_helper.rb | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/support/vm/activedirectory/env.sh.example b/test/support/vm/activedirectory/env.sh.example index 5a17fe8..3ca2c9b 100644 --- a/test/support/vm/activedirectory/env.sh.example +++ b/test/support/vm/activedirectory/env.sh.example @@ -1,8 +1,8 @@ # Copy this to ad-env.sh, and fill in with your own values export TESTENV=activedirectory -export ACTIVEDIRECTORY_HOST=123.123.123.123 -export ACTIVEDIRECTORY_PORT=389 -export ACTIVEDIRECTORY_USER="CN=Administrator,CN=Users,DC=ad,DC=example,DC=com" -export ACTIVEDIRECTORY_PASSWORD='passworD1' -export ACTIVEDIRECTORY_SEARCH_DOMAINS='CN=Users,DC=ad,DC=ghe,DC=local' +export INTEGRATION_HOST=123.123.123.123 +export INTEGRATION_PORT=389 +export INTEGRATION_USER="CN=Administrator,CN=Users,DC=ad,DC=example,DC=com" +export INTEGRATION_PASSWORD='passworD1' +export INTEGRATION_SEARCH_DOMAINS='CN=Users,DC=example,DC=com' diff --git a/test/support/vm/activedirectory/reset-env.sh b/test/support/vm/activedirectory/reset-env.sh index 6bfab09..971423f 100644 --- a/test/support/vm/activedirectory/reset-env.sh +++ b/test/support/vm/activedirectory/reset-env.sh @@ -1,6 +1,6 @@ unset TESTENV -unset ACTIVEDIRECTORY_HOST -unset ACTIVEDIRECTORY_PORT -unset ACTIVEDIRECTORY_USER -unset ACTIVEDIRECTORY_PASSWORD -unset ACTIVEDIRECTORY_SEARCH_DOMAINS +unset INTEGRATION_HOST +unset INTEGRATION_PORT +unset INTEGRATION_USER +unset INTEGRATION_PASSWORD +unset INTEGRATION_SEARCH_DOMAINS diff --git a/test/support/vm/openldap/README.md b/test/support/vm/openldap/README.md index 67a4ded..ced5a63 100644 --- a/test/support/vm/openldap/README.md +++ b/test/support/vm/openldap/README.md @@ -16,10 +16,10 @@ $ ip=$(vagrant ssh -- "ifconfig eth1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9] $ cd ../../../.. # run all tests against OpenLDAP -$ time TESTENV=openldap OPENLDAP_HOST=$ip bundle exec rake +$ time TESTENV=openldap INTEGRATION_HOST=$ip bundle exec rake # run a specific test file against OpenLDAP -$ time TESTENV=openldap OPENLDAP_HOST=$ip bundle exec ruby test/membership_validators/recursive_test.rb +$ time TESTENV=openldap INTEGRATION_HOST=$ip bundle exec ruby test/membership_validators/recursive_test.rb # run OpenLDAP tests by default $ export TESTENV=openldap diff --git a/test/test_helper.rb b/test/test_helper.rb index 1f828c4..5beca09 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -71,7 +71,7 @@ def options instrumentation_service: @service when "openldap" { - host: ENV.fetch("OPENLDAP_HOST", "localhost"), + host: ENV.fetch("INTEGRATION_HOST", "localhost"), port: 389, admin_user: 'uid=admin,dc=github,dc=com', admin_password: 'passworD1', @@ -81,11 +81,11 @@ def options } when "activedirectory" { - host: ENV.fetch("ACTIVEDIRECTORY_HOST"), - port: ENV.fetch("ACTIVEDIRECTORY_PORT", 389), - admin_user: ENV.fetch("ACTIVEDIRECTORY_USER"), - admin_password: ENV.fetch("ACTIVEDIRECTORY_PASSWORD"), - search_domains: ENV.fetch("ACTIVEDIRECTORY_SEARCH_DOMAINS"), + host: ENV.fetch("INTEGRATION_HOST"), + port: ENV.fetch("INTEGRATION_PORT", 389), + admin_user: ENV.fetch("INTEGRATION_USER"), + admin_password: ENV.fetch("INTEGRATION_PASSWORD"), + search_domains: ENV.fetch("INTEGRATION_SEARCH_DOMAINS"), instrumentation_service: @service } end From 9c6527b51a8f454308438b95338a62add8ab1d4a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 15:33:45 -0800 Subject: [PATCH 106/307] Bump version to 1.5.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index a3b1a20..0033761 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.4.0" + spec.version = "1.5.0" spec.authors = ["David Calavera"] spec.email = ["david.calavera@gmail.com"] spec.description = %q{Ldap authentication for humans} From c7fe88e22e4a1981ec9d54983e96a8a88418a6b8 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 15:34:10 -0800 Subject: [PATCH 107/307] Fix script/changelog --- script/changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/changelog b/script/changelog index 6ee2a54..8df90b0 100755 --- a/script/changelog +++ b/script/changelog @@ -13,16 +13,16 @@ set -e [ $# -eq 0 ] && set -- --help # parse args -repo=$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4-) +repo=$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4- | sed 's/\.git//') base=$(git tag -l | sort -n | tail -n 1) head="HEAD" api_url="https://api.github.com" -echo "# $base..$head" +echo "# $repo $base..$head" echo # get merged PR's. Better way is to query the API for these, but this is easier -for pr in $(git log --oneline v1.3.6..HEAD | grep "Merge pull request" | awk '{gsub("#",""); print $5}') +for pr in $(git log --oneline $base..$head | grep "Merge pull request" | awk '{gsub("#",""); print $5}') do # frustrated with trying to pull out the right values, fell back to ruby curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} [##{pr[%q(number)]}](#{pr[%q(html_url)]})"' From ddeb404dfa3e1c498b0b82f9bacca699a953d7ab Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 15:39:16 -0800 Subject: [PATCH 108/307] Document v1.5.0 changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d843d2f..6c108b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v1.5.0 + +* Automatically detect membership validator strategy by default [#58](https://github.com/github/github-ldap/pull/58) [#62](https://github.com/github/github-ldap/pull/62) +* Document local integration testing with Active Directory [#61](https://github.com/github/github-ldap/pull/61) + ## v1.4.0 * Document constructor options [#57](https://github.com/github/github-ldap/pull/57) From f7448ee6d146cb7c8c42433aa4d44745fec20843 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 14 Nov 2014 16:02:47 -0800 Subject: [PATCH 109/307] Release 1.5.0 From 0dd969d6386bfd4f9889d39acb193306a344b6cf Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 24 Nov 2014 18:29:40 -0800 Subject: [PATCH 110/307] Add WIP recursive member search strategy --- lib/github/ldap.rb | 1 + lib/github/ldap/members.rb | 20 ++++++ lib/github/ldap/members/recursive.rb | 94 ++++++++++++++++++++++++++++ test/members/recursive_test.rb | 24 +++++++ 4 files changed, 139 insertions(+) create mode 100644 lib/github/ldap/members.rb create mode 100644 lib/github/ldap/members/recursive.rb create mode 100644 test/members/recursive_test.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 3258ac4..43c3f3b 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -9,6 +9,7 @@ class Ldap require 'github/ldap/virtual_group' require 'github/ldap/virtual_attributes' require 'github/ldap/instrumentation' + require 'github/ldap/members' require 'github/ldap/membership_validators' include Instrumentation diff --git a/lib/github/ldap/members.rb b/lib/github/ldap/members.rb new file mode 100644 index 0000000..65a2539 --- /dev/null +++ b/lib/github/ldap/members.rb @@ -0,0 +1,20 @@ +require 'github/ldap/members/recursive' + +module GitHub + class Ldap + # Provides various strategies for member lookup. + # + # For example: + # + # group = domain.groups(%w(Engineering)).first + # strategy = GitHub::Ldap::Members::Recursive.new(ldap) + # strategy.perform(group) #=> [#] + # + module Members + # Internal: Mapping of strategy name to class. + STRATEGIES = { + :recursive => GitHub::Ldap::Members::Recursive + } + end + end +end diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb new file mode 100644 index 0000000..7de20c9 --- /dev/null +++ b/lib/github/ldap/members/recursive.rb @@ -0,0 +1,94 @@ +module GitHub + class Ldap + module Members + # Look up group members recursively. + # + # In this case, we're returning User Net::LDAP::Entry objects, not entries + # for LDAP Groups. + # + # This results in a maximum of `depth` queries (per domain) to look up + # members of a group and its subgroups. + class Recursive + include Filter + + DEFAULT_MAX_DEPTH = 9 + ATTRS = %w(dn cn) + + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Public: Instantiate new search strategy. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options + def initialize(ldap, options = {}) + @ldap = ldap + @options = options + end + + # Internal: Domains to search through. + # + # Returns an Array of GitHub::Ldap::Domain objects. + def domains + @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } + end + private :domains + + # Public: Performs search for group members, including members of + # subgroups recursively. + # + # Returns Array of Net::LDAP::Entry objects. + def perform(group, depth = DEFAULT_MAX_DEPTH) + members = Hash.new + + member_dns = group["member"] + + domains.each do |domain| + # find members + entries = domain.search(filter: membership_filter(member_dns), attributes: ATTRS) + + next if entries.empty? + + return entries + end + + [] + end + + # Internal: Construct a filter to find groups this entry is a direct + # member of. + # + # Overloads the included `GitHub::Ldap::Filters#member_filter` method + # to inject `posixGroup` handling. + # + # Returns a Net::LDAP::Filter object. + def member_filter(entry_or_uid, uid = ldap.uid) + filter = super(entry_or_uid) + + if ldap.posix_support_enabled? + if posix_filter = posix_member_filter(entry_or_uid, uid) + filter |= posix_filter + end + end + + filter + end + + # Internal: Construct a filter to find groups whose members are the + # Array of String group DNs passed in. + # + # Returns a String filter. + def membership_filter(groups) + groups.map { |entry| member_filter(entry, :cn) }.reduce(:|) + end + + # Internal: the group DNs to check against. + # + # Returns an Array of String DNs. + def group_dns + @group_dns ||= groups.map(&:dn) + end + end + end + end +end diff --git a/test/members/recursive_test.rb b/test/members/recursive_test.rb new file mode 100644 index 0000000..ddcf866 --- /dev/null +++ b/test/members/recursive_test.rb @@ -0,0 +1,24 @@ +require_relative '../test_helper' + +class GitHubLdapRecursiveMembersTest < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @strategy = GitHub::Ldap::Members::Recursive.new(@ldap) + end + + def find_group(cn) + @domain.groups([cn]).first + end + + def test_finds_group_members + members = @strategy.perform(find_group("nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end +end From 3892f4e75011a3ba4b78f3d3cd10816c090f0489 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 24 Nov 2014 19:47:18 -0800 Subject: [PATCH 111/307] Implement recursive group member search --- lib/github/ldap/domain.rb | 7 ++- lib/github/ldap/members/recursive.rb | 87 +++++++++++++++------------- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index aa2066b..8fd904f 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -163,8 +163,11 @@ def search(options, &block) # Get the entry for this domain. # # Returns a Net::LDAP::Entry - def bind - search(size: 1, scope: Net::LDAP::SearchScope_BaseObject).first + def bind(options = {}) + options[:size] = 1 + options[:scope] = Net::LDAP::SearchScope_BaseObject + options[:attributes] ||= [] + search(options).first end end end diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 7de20c9..15183c8 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -12,11 +12,14 @@ class Recursive include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(dn cn) + ATTRS = %w(dn cn member) # Internal: The GitHub::Ldap object to search domains with. attr_reader :ldap + # Internal: The maximum depth to search for members. + attr_reader :depth + # Public: Instantiate new search strategy. # # - ldap: GitHub::Ldap object @@ -24,6 +27,7 @@ class Recursive def initialize(ldap, options = {}) @ldap = ldap @options = options + @depth = options[:depth] || DEFAULT_MAX_DEPTH end # Internal: Domains to search through. @@ -34,59 +38,64 @@ def domains end private :domains - # Public: Performs search for group members, including members of - # subgroups recursively. + # Public: Performs search for group members, including groups and + # members of subgroups recursively. # # Returns Array of Net::LDAP::Entry objects. - def perform(group, depth = DEFAULT_MAX_DEPTH) - members = Hash.new - - member_dns = group["member"] + def perform(group) + found = Hash.new - domains.each do |domain| - # find members - entries = domain.search(filter: membership_filter(member_dns), attributes: ATTRS) + members = group["member"] + return [] if members.empty? - next if entries.empty? + # find members (N queries) + entries = entries_by_dn(members) + return [] if entries.empty? - return entries + # track found entries + entries.each do |entry| + found[entry.dn] = entry end - [] - end + # descend to `depth` levels, at most + depth.times do |n| + # find every (new, unique) member entry + depth_subentries = entries.each_with_object([]) do |entry, depth_entries| + submembers = entry["member"] - # Internal: Construct a filter to find groups this entry is a direct - # member of. - # - # Overloads the included `GitHub::Ldap::Filters#member_filter` method - # to inject `posixGroup` handling. - # - # Returns a Net::LDAP::Filter object. - def member_filter(entry_or_uid, uid = ldap.uid) - filter = super(entry_or_uid) + # skip any members we've already found + submembers.reject! { |dn| found.key?(dn) } + + next if submembers.empty? + + # find members of subgroup, including subgroups (N queries) + subentries = entries_by_dn(submembers) - if ldap.posix_support_enabled? - if posix_filter = posix_member_filter(entry_or_uid, uid) - filter |= posix_filter + # track found subentries + subentries.each { |entry| found[entry.dn] = entry } + + # collect all entries for this depth + depth_entries.concat subentries end - end - filter - end + # stop if there are no more subgroups to search + break if depth_subentries.empty? - # Internal: Construct a filter to find groups whose members are the - # Array of String group DNs passed in. - # - # Returns a String filter. - def membership_filter(groups) - groups.map { |entry| member_filter(entry, :cn) }.reduce(:|) + # go one level deeper + entries = depth_subentries + end + + # return all found entries + found.values end - # Internal: the group DNs to check against. + # Internal: Bind a list of DNs to their respective entries. # - # Returns an Array of String DNs. - def group_dns - @group_dns ||= groups.map(&:dn) + # Returns an Array of Net::LDAP::Entry objects. + def entries_by_dn(members) + members.map do |dn| + ldap.domain(dn).bind(attributes: ATTRS) + end.compact end end end From 19b60bdf84c8c5bdc184d75cd29c86f0ba95d0ad Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 24 Nov 2014 22:13:05 -0800 Subject: [PATCH 112/307] Clean up unneeded bits --- lib/github/ldap/members/recursive.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 15183c8..7e496f4 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -3,16 +3,13 @@ class Ldap module Members # Look up group members recursively. # - # In this case, we're returning User Net::LDAP::Entry objects, not entries - # for LDAP Groups. - # - # This results in a maximum of `depth` queries (per domain) to look up + # This results in a maximum of `depth` iterations/recursions to look up # members of a group and its subgroups. class Recursive include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(dn cn member) + ATTRS = %w(dn member) # Internal: The GitHub::Ldap object to search domains with. attr_reader :ldap @@ -30,14 +27,6 @@ def initialize(ldap, options = {}) @depth = options[:depth] || DEFAULT_MAX_DEPTH end - # Internal: Domains to search through. - # - # Returns an Array of GitHub::Ldap::Domain objects. - def domains - @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } - end - private :domains - # Public: Performs search for group members, including groups and # members of subgroups recursively. # From 9f48809ca3ae23a766d0589f015e1384bc4a0ccd Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 24 Nov 2014 22:13:20 -0800 Subject: [PATCH 113/307] Add Classic members search strategy --- lib/github/ldap/members.rb | 2 ++ lib/github/ldap/members/classic.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 lib/github/ldap/members/classic.rb diff --git a/lib/github/ldap/members.rb b/lib/github/ldap/members.rb index 65a2539..85b7f37 100644 --- a/lib/github/ldap/members.rb +++ b/lib/github/ldap/members.rb @@ -1,3 +1,4 @@ +require 'github/ldap/members/classic' require 'github/ldap/members/recursive' module GitHub @@ -13,6 +14,7 @@ class Ldap module Members # Internal: Mapping of strategy name to class. STRATEGIES = { + :classic => GitHub::Ldap::Members::Classic, :recursive => GitHub::Ldap::Members::Recursive } end diff --git a/lib/github/ldap/members/classic.rb b/lib/github/ldap/members/classic.rb new file mode 100644 index 0000000..81a5601 --- /dev/null +++ b/lib/github/ldap/members/classic.rb @@ -0,0 +1,30 @@ +module GitHub + class Ldap + module Members + # Look up group members using the existing `Group#members` and + # `Group#subgroups` API. + class Classic + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Public: Instantiate new search strategy. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options (unused) + def initialize(ldap, options = {}) + @ldap = ldap + @options = options + end + + # Public: Performs search for group members, including groups and + # members of subgroups recursively. + # + # Returns Array of Net::LDAP::Entry objects. + def perform(group_entry) + group = ldap.load_group(group_entry) + group.members + group.subgroups + end + end + end + end +end From 15b47712784455de4baee7d970e7bb8065e4e586 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:13:13 -0800 Subject: [PATCH 114/307] Test deep recursion, configurable depth limiting --- test/members/recursive_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/members/recursive_test.rb b/test/members/recursive_test.rb index ddcf866..d34f3bb 100644 --- a/test/members/recursive_test.rb +++ b/test/members/recursive_test.rb @@ -21,4 +21,15 @@ def test_finds_nested_group_members members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn) assert_includes members, @entry.dn end + + def test_finds_deeply_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_respects_configured_depth_limit + strategy = GitHub::Ldap::Members::Recursive.new(@ldap, depth: 2) + members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + refute_includes members, @entry.dn + end end From 7129554127a752aa18507cf914effc8caee4a6e6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:32:30 -0800 Subject: [PATCH 115/307] Include uniqueMember, memberUid in attrs to fetch --- lib/github/ldap/members/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 7e496f4..ab915af 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -9,7 +9,7 @@ class Recursive include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(dn member) + ATTRS = %w(dn member uniqueMember memberUid) # Internal: The GitHub::Ldap object to search domains with. attr_reader :ldap From b01e0ea87fb5a8d84a21989a7ea939f6d69a03c1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:33:46 -0800 Subject: [PATCH 116/307] Refactor member entry search, add memberUid search, test --- lib/github/ldap/members/recursive.rb | 49 ++++++++++++++++++++++++---- test/members/recursive_test.rb | 5 +++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index ab915af..683bec5 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -34,11 +34,8 @@ def initialize(ldap, options = {}) def perform(group) found = Hash.new - members = group["member"] - return [] if members.empty? - # find members (N queries) - entries = entries_by_dn(members) + entries = member_entries(group) return [] if entries.empty? # track found entries @@ -55,10 +52,9 @@ def perform(group) # skip any members we've already found submembers.reject! { |dn| found.key?(dn) } - next if submembers.empty? - # find members of subgroup, including subgroups (N queries) - subentries = entries_by_dn(submembers) + subentries = member_entries(entry) + next if subentries.empty? # track found subentries subentries.each { |entry| found[entry.dn] = entry } @@ -78,6 +74,17 @@ def perform(group) found.values end + # Internal: Fetch member entries, including subgroups, for the given + # entry. + # + # Returns an Array of Net::LDAP::Entry objects. + def member_entries(entry) + dns = member_dns(entry) + return [] if dns.empty? + + entries_by_dn(dns) + end + # Internal: Bind a list of DNs to their respective entries. # # Returns an Array of Net::LDAP::Entry objects. @@ -86,6 +93,34 @@ def entries_by_dn(members) ldap.domain(dn).bind(attributes: ATTRS) end.compact end + + def entries_by_uid(members) + filter = members.map { |uid| Net::LDAP::Filter.eq(ldap.uid, uid) }.reduce(:|) + domains.each_with_object([]) do |domain, entries| + entries.concat domain.search(filter: filter, attributes: ATTRS) + end.compact + end + + # Internal: Returns an Array of String DNs for `groupOfNames` and + # `uniqueGroupOfNames` members. + def member_dns(entry) + MEMBERSHIP_NAMES.each_with_object([]) do |attr_name, members| + members.concat entry[attr_name] + end + end + + # Internal: Returns an Array of String UIDs for PosixGroups members. + def member_uids(entry) + entry["memberUid"] + end + + # Internal: Domains to search through. + # + # Returns an Array of GitHub::Ldap::Domain objects. + def domains + @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } + end + private :domains end end end diff --git a/test/members/recursive_test.rb b/test/members/recursive_test.rb index d34f3bb..e743ca8 100644 --- a/test/members/recursive_test.rb +++ b/test/members/recursive_test.rb @@ -27,6 +27,11 @@ def test_finds_deeply_nested_group_members assert_includes members, @entry.dn end + def test_finds_posix_group_members + members = @strategy.perform(find_group("posix-group1")).map(&:dn) + assert_includes members, @entry.dn + end + def test_respects_configured_depth_limit strategy = GitHub::Ldap::Members::Recursive.new(@ldap, depth: 2) members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) From 3822f4bb1739d163354ee6c6a5de5c8c14b4b762 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:36:54 -0800 Subject: [PATCH 117/307] Fetch member entries by DN, UID --- lib/github/ldap/members/recursive.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 683bec5..2ca10bf 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -79,10 +79,14 @@ def perform(group) # # Returns an Array of Net::LDAP::Entry objects. def member_entries(entry) - dns = member_dns(entry) - return [] if dns.empty? + entries = [] + dns = member_dns(entry) + uids = member_uids(entry) - entries_by_dn(dns) + entries.concat entries_by_uid(uids) unless uids.empty? + entries.concat entries_by_dn(dns) unless dns.empty? + + entries end # Internal: Bind a list of DNs to their respective entries. From 9418bd06ba399392e5f8077520bc9455f0b91bee Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:39:11 -0800 Subject: [PATCH 118/307] Doc entries_by_uid method, make internal methods private --- lib/github/ldap/members/recursive.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 2ca10bf..0e1bef9 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -88,6 +88,7 @@ def member_entries(entry) entries end + private :member_entries # Internal: Bind a list of DNs to their respective entries. # @@ -97,13 +98,18 @@ def entries_by_dn(members) ldap.domain(dn).bind(attributes: ATTRS) end.compact end + private :entries_by_dn + # Internal: Fetch entries by UID. + # + # Returns an Array of Net::LDAP::Entry objects. def entries_by_uid(members) filter = members.map { |uid| Net::LDAP::Filter.eq(ldap.uid, uid) }.reduce(:|) domains.each_with_object([]) do |domain, entries| entries.concat domain.search(filter: filter, attributes: ATTRS) end.compact end + private :entries_by_uid # Internal: Returns an Array of String DNs for `groupOfNames` and # `uniqueGroupOfNames` members. @@ -112,11 +118,13 @@ def member_dns(entry) members.concat entry[attr_name] end end + private :member_dns # Internal: Returns an Array of String UIDs for PosixGroups members. def member_uids(entry) entry["memberUid"] end + private :member_uids # Internal: Domains to search through. # From 76546821dd458009bcbda22024f9e6ab5fa101ca Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 25 Nov 2014 16:41:44 -0800 Subject: [PATCH 119/307] Test classic member search strategy --- test/members/classic_test.rb | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/members/classic_test.rb diff --git a/test/members/classic_test.rb b/test/members/classic_test.rb new file mode 100644 index 0000000..6c36a3f --- /dev/null +++ b/test/members/classic_test.rb @@ -0,0 +1,40 @@ +require_relative '../test_helper' + +class GitHubLdapRecursiveMembersTest < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @strategy = GitHub::Ldap::Members::Classic.new(@ldap) + end + + def find_group(cn) + @domain.groups([cn]).first + end + + def test_finds_group_members + members = @strategy.perform(find_group("nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_deeply_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_posix_group_members + members = @strategy.perform(find_group("posix-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_does_not_respect_configured_depth_limit + strategy = GitHub::Ldap::Members::Classic.new(@ldap, depth: 2) + members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + assert_includes members, @entry.dn + end +end From 4cc4caa22c44e9fe7f51b391923eff191efc5b17 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 28 Nov 2014 18:05:27 -0800 Subject: [PATCH 120/307] Querying for the DN attr is redundant/unnecessary --- lib/github/ldap/members/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 0e1bef9..867dda0 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -9,7 +9,7 @@ class Recursive include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(dn member uniqueMember memberUid) + ATTRS = %w(member uniqueMember memberUid) # Internal: The GitHub::Ldap object to search domains with. attr_reader :ldap From e4242e549ac31bcfcf86c978b022217faae8f98f Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sat, 29 Nov 2014 15:41:22 -0800 Subject: [PATCH 121/307] Expose Group.group? for testing if entry is group --- lib/github/ldap/group.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/group.rb b/lib/github/ldap/group.rb index 25066a0..633e034 100644 --- a/lib/github/ldap/group.rb +++ b/lib/github/ldap/group.rb @@ -69,6 +69,11 @@ def member_names end end + # Internal: Returns true if the object class(es) provided match a group's. + def group?(object_class) + self.class.group?(object_class) + end + # Internal - Check if an object class includes the member names # Use `&` rathen than `include?` because both are arrays. # @@ -76,7 +81,7 @@ def member_names # will fail to match correctly unless we also downcase our group classes. # # Returns true if the object class includes one of the group class names. - def group?(object_class) + def self.group?(object_class) !(GROUP_CLASS_NAMES.map(&:downcase) & object_class.map(&:downcase)).empty? end From 8056ecd74133bfc7938176ebc0f51067bf63f405 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sat, 29 Nov 2014 15:42:17 -0800 Subject: [PATCH 122/307] Take attrs option for Member search strategy --- lib/github/ldap/members/recursive.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/members/recursive.rb index 867dda0..9b0becc 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/members/recursive.rb @@ -9,7 +9,7 @@ class Recursive include Filter DEFAULT_MAX_DEPTH = 9 - ATTRS = %w(member uniqueMember memberUid) + DEFAULT_ATTRS = %w(member uniqueMember memberUid) # Internal: The GitHub::Ldap object to search domains with. attr_reader :ldap @@ -17,6 +17,9 @@ class Recursive # Internal: The maximum depth to search for members. attr_reader :depth + # Internal: The attributes to search for. + attr_reader :attrs + # Public: Instantiate new search strategy. # # - ldap: GitHub::Ldap object @@ -25,6 +28,7 @@ def initialize(ldap, options = {}) @ldap = ldap @options = options @depth = options[:depth] || DEFAULT_MAX_DEPTH + @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS end # Public: Performs search for group members, including groups and @@ -95,7 +99,7 @@ def member_entries(entry) # Returns an Array of Net::LDAP::Entry objects. def entries_by_dn(members) members.map do |dn| - ldap.domain(dn).bind(attributes: ATTRS) + ldap.domain(dn).bind(attributes: attrs) end.compact end private :entries_by_dn @@ -106,7 +110,7 @@ def entries_by_dn(members) def entries_by_uid(members) filter = members.map { |uid| Net::LDAP::Filter.eq(ldap.uid, uid) }.reduce(:|) domains.each_with_object([]) do |domain, entries| - entries.concat domain.search(filter: filter, attributes: ATTRS) + entries.concat domain.search(filter: filter, attributes: attrs) end.compact end private :entries_by_uid From 12ba663ed4f53b46c552913993407ae4a58f56c2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 15:23:22 -0800 Subject: [PATCH 123/307] Rename Members to MemberSearch --- lib/github/ldap.rb | 2 +- lib/github/ldap/{members.rb => member_search.rb} | 12 ++++++------ .../ldap/{members => member_search}/classic.rb | 2 +- .../ldap/{members => member_search}/recursive.rb | 2 +- test/{members => member_search}/classic_test.rb | 6 +++--- test/{members => member_search}/recursive_test.rb | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) rename lib/github/ldap/{members.rb => member_search.rb} (52%) rename lib/github/ldap/{members => member_search}/classic.rb (97%) rename lib/github/ldap/{members => member_search}/recursive.rb (99%) rename test/{members => member_search}/classic_test.rb (84%) rename test/{members => member_search}/recursive_test.rb (84%) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 43c3f3b..40db8be 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -9,7 +9,7 @@ class Ldap require 'github/ldap/virtual_group' require 'github/ldap/virtual_attributes' require 'github/ldap/instrumentation' - require 'github/ldap/members' + require 'github/ldap/member_search' require 'github/ldap/membership_validators' include Instrumentation diff --git a/lib/github/ldap/members.rb b/lib/github/ldap/member_search.rb similarity index 52% rename from lib/github/ldap/members.rb rename to lib/github/ldap/member_search.rb index 85b7f37..10e0b38 100644 --- a/lib/github/ldap/members.rb +++ b/lib/github/ldap/member_search.rb @@ -1,5 +1,5 @@ -require 'github/ldap/members/classic' -require 'github/ldap/members/recursive' +require 'github/ldap/member_search/classic' +require 'github/ldap/member_search/recursive' module GitHub class Ldap @@ -8,14 +8,14 @@ class Ldap # For example: # # group = domain.groups(%w(Engineering)).first - # strategy = GitHub::Ldap::Members::Recursive.new(ldap) + # strategy = GitHub::Ldap::MemberSearch::Recursive.new(ldap) # strategy.perform(group) #=> [#] # - module Members + module MemberSearch # Internal: Mapping of strategy name to class. STRATEGIES = { - :classic => GitHub::Ldap::Members::Classic, - :recursive => GitHub::Ldap::Members::Recursive + :classic => GitHub::Ldap::MemberSearch::Classic, + :recursive => GitHub::Ldap::MemberSearch::Recursive } end end diff --git a/lib/github/ldap/members/classic.rb b/lib/github/ldap/member_search/classic.rb similarity index 97% rename from lib/github/ldap/members/classic.rb rename to lib/github/ldap/member_search/classic.rb index 81a5601..68a860b 100644 --- a/lib/github/ldap/members/classic.rb +++ b/lib/github/ldap/member_search/classic.rb @@ -1,6 +1,6 @@ module GitHub class Ldap - module Members + module MemberSearch # Look up group members using the existing `Group#members` and # `Group#subgroups` API. class Classic diff --git a/lib/github/ldap/members/recursive.rb b/lib/github/ldap/member_search/recursive.rb similarity index 99% rename from lib/github/ldap/members/recursive.rb rename to lib/github/ldap/member_search/recursive.rb index 9b0becc..0df6a1c 100644 --- a/lib/github/ldap/members/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -1,6 +1,6 @@ module GitHub class Ldap - module Members + module MemberSearch # Look up group members recursively. # # This results in a maximum of `depth` iterations/recursions to look up diff --git a/test/members/classic_test.rb b/test/member_search/classic_test.rb similarity index 84% rename from test/members/classic_test.rb rename to test/member_search/classic_test.rb index 6c36a3f..656e12b 100644 --- a/test/members/classic_test.rb +++ b/test/member_search/classic_test.rb @@ -1,11 +1,11 @@ require_relative '../test_helper' -class GitHubLdapRecursiveMembersTest < GitHub::Ldap::Test +class GitHubLdapRecursiveMemberSearchTest < GitHub::Ldap::Test def setup @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") @entry = @domain.user?('user1') - @strategy = GitHub::Ldap::Members::Classic.new(@ldap) + @strategy = GitHub::Ldap::MemberSearch::Classic.new(@ldap) end def find_group(cn) @@ -33,7 +33,7 @@ def test_finds_posix_group_members end def test_does_not_respect_configured_depth_limit - strategy = GitHub::Ldap::Members::Classic.new(@ldap, depth: 2) + strategy = GitHub::Ldap::MemberSearch::Classic.new(@ldap, depth: 2) members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) assert_includes members, @entry.dn end diff --git a/test/members/recursive_test.rb b/test/member_search/recursive_test.rb similarity index 84% rename from test/members/recursive_test.rb rename to test/member_search/recursive_test.rb index e743ca8..a2d388d 100644 --- a/test/members/recursive_test.rb +++ b/test/member_search/recursive_test.rb @@ -1,11 +1,11 @@ require_relative '../test_helper' -class GitHubLdapRecursiveMembersTest < GitHub::Ldap::Test +class GitHubLdapRecursiveMemberSearchTest < GitHub::Ldap::Test def setup @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") @entry = @domain.user?('user1') - @strategy = GitHub::Ldap::Members::Recursive.new(@ldap) + @strategy = GitHub::Ldap::MemberSearch::Recursive.new(@ldap) end def find_group(cn) @@ -33,7 +33,7 @@ def test_finds_posix_group_members end def test_respects_configured_depth_limit - strategy = GitHub::Ldap::Members::Recursive.new(@ldap, depth: 2) + strategy = GitHub::Ldap::MemberSearch::Recursive.new(@ldap, depth: 2) members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) refute_includes members, @entry.dn end From e79d6321ea90e0c7b8111c2cac07546b87825687 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 15:41:05 -0800 Subject: [PATCH 124/307] Add member_search_strategy config option --- lib/github/ldap.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 40db8be..e4c8c55 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -36,6 +36,7 @@ class Ldap attr_reader :uid, :search_domains, :virtual_attributes, :membership_validator, + :member_search_strategy, :instrumentation_service # Build a new GitHub::Ldap instance @@ -92,6 +93,9 @@ def initialize(options = {}) # configure which strategy should be used to validate user membership configure_membership_validation_strategy(options[:membership_validator]) + # configure which strategy should be used for member search + configure_member_search_strategy(options[:member_search_strategy]) + # enables instrumenting queries @instrumentation_service = options[:instrumentation_service] end @@ -255,5 +259,24 @@ def configure_membership_validation_strategy(strategy = nil) :detect end end + + # Internal: Configure the member search strategy. + # + # Used by GitHub::Ldap::MemberSearch::Detect to force a specific strategy + # (instead of detecting the host capabilities and deciding at runtime). + # + # If `strategy` is not provided, or doesn't match a known strategy, + # defaults to `:detect`. Otherwise the configured strategy is selected. + # + # Returns the selected strategy Symbol. + def configure_member_search_strategy(strategy = nil) + @member_search_strategy = + case strategy.to_s + when "classic", "recursive" + strategy.to_sym + else + :detect + end + end end end From a2ea06f1976779fc5c220bb0bb1560b8cc69a758 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 15:51:57 -0800 Subject: [PATCH 125/307] Extract re-usable capability detection logic --- lib/github/ldap.rb | 1 + lib/github/ldap/capabilities.rb | 24 +++++++++++++++++++ .../ldap/membership_validators/detect.rb | 20 ++-------------- 3 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 lib/github/ldap/capabilities.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index e4c8c55..dc18a44 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -9,6 +9,7 @@ class Ldap require 'github/ldap/virtual_group' require 'github/ldap/virtual_attributes' require 'github/ldap/instrumentation' + require 'github/ldap/capabilities' require 'github/ldap/member_search' require 'github/ldap/membership_validators' diff --git a/lib/github/ldap/capabilities.rb b/lib/github/ldap/capabilities.rb new file mode 100644 index 0000000..370be4b --- /dev/null +++ b/lib/github/ldap/capabilities.rb @@ -0,0 +1,24 @@ +module GitHub + class Ldap + module Capabilities + # Internal: The capability required to use the ActiveDirectory strategy. + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze + + # Internal: Detect whether the LDAP host is an ActiveDirectory server. + # + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + # + # Returns true if the host is an ActiveDirectory server, false otherwise. + def active_directory_capability? + capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) + end + + # Internal: Returns the Net::LDAP::Entry object describing the LDAP + # host's capabilities (via the Root DSE). + def capabilities + ldap.capabilities + end + end + end +end diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb index ba8c4ba..74f7f0d 100644 --- a/lib/github/ldap/membership_validators/detect.rb +++ b/lib/github/ldap/membership_validators/detect.rb @@ -7,9 +7,8 @@ module MembershipValidators # also be defined via `GitHub::Ldap#membership_validator=`. See also # `GitHub::Ldap#configure_membership_validation_strategy`. class Detect < Base - # Internal: The capability required to use the ActiveDirectory strategy. - # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze + # Defines `active_directory_capability?` and necessary helpers. + include GitHub::Ldap::Capabilities def perform(entry) # short circuit validation if there are no groups to check against @@ -48,21 +47,6 @@ def detect_strategy def strategy_config ldap.membership_validator end - - # Internal: Detect whether the LDAP host is an ActiveDirectory server. - # - # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - # - # Returns true if the host is an ActiveDirectory server, false otherwise. - def active_directory_capability? - capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) - end - - # Internal: Returns the Net::LDAP::Entry object describing the LDAP - # host's capabilities (via the Root DSE). - def capabilities - ldap.capabilities - end end end end From 05cb39cb9b860a7cfdb0b3b521e6ee8856dc8806 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 15:59:29 -0800 Subject: [PATCH 126/307] Add member search Detect strategy --- lib/github/ldap/member_search.rb | 1 + lib/github/ldap/member_search/detect.rb | 71 +++++++++++++++++++++++++ test/member_search/detect_test.rb | 42 +++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 lib/github/ldap/member_search/detect.rb create mode 100644 test/member_search/detect_test.rb diff --git a/lib/github/ldap/member_search.rb b/lib/github/ldap/member_search.rb index 10e0b38..fc81607 100644 --- a/lib/github/ldap/member_search.rb +++ b/lib/github/ldap/member_search.rb @@ -1,3 +1,4 @@ +require 'github/ldap/member_search/detect' require 'github/ldap/member_search/classic' require 'github/ldap/member_search/recursive' diff --git a/lib/github/ldap/member_search/detect.rb b/lib/github/ldap/member_search/detect.rb new file mode 100644 index 0000000..06cdb32 --- /dev/null +++ b/lib/github/ldap/member_search/detect.rb @@ -0,0 +1,71 @@ +module GitHub + class Ldap + module MemberSearch + # Detects the LDAP host's capabilities and determines the appropriate + # member search strategy at runtime. + # + # Currently detects for ActiveDirectory in-chain membership validation. + # + # An explicit strategy can also be defined via + # `GitHub::Ldap#member_search_strategy=`. + # + # See also `GitHub::Ldap#configure_member_search_strategy`. + class Detect + # Defines `active_directory_capability?` and necessary helpers. + include GitHub::Ldap::Capabilities + + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Internal: The Hash of options to pass through to the strategy. + attr_reader :options + + # Public: Instantiate a meta strategy to detect the right strategy + # to use for the search, and call that strategy, at runtime. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options (passed through) + def initialize(ldap, options = {}) + @ldap = ldap + @options = options + end + + # Public: Performs search for group members via the appropriate search + # strategy detected/configured. + # + # Returns Array of Net::LDAP::Entry objects. + def perform(entry) + strategy.perform(entry) + end + + # Internal: Returns the member search strategy object. + def strategy + @strategy ||= begin + strategy = detect_strategy + strategy.new(ldap, options) + end + end + + # Internal: Find the most appropriate search strategy, either by + # configuration or by detecting the host's capabilities. + # + # Returns the strategy class. + def detect_strategy + case + when GitHub::Ldap::MemberSearch::STRATEGIES.key?(strategy_config) + GitHub::Ldap::MemberSearch::STRATEGIES[strategy_config] + when active_directory_capability? + GitHub::Ldap::MemberSearch::STRATEGIES[:active_directory] + else + GitHub::Ldap::MemberSearch::STRATEGIES[:recursive] + end + end + + # Internal: Returns the configured member search strategy Symbol. + def strategy_config + ldap.member_search_strategy + end + end + end + end +end diff --git a/test/member_search/detect_test.rb b/test/member_search/detect_test.rb new file mode 100644 index 0000000..6092cdf --- /dev/null +++ b/test/member_search/detect_test.rb @@ -0,0 +1,42 @@ +require_relative '../test_helper' + +# NOTE: Since this strategy is targeted at detecting ActiveDirectory +# capabilities, and we don't have AD setup in CI, we stub out actual queries +# and test against what AD *would* respond with. + +class GitHubLdapDetectMemberSearchTest < GitHub::Ldap::Test + include GitHub::Ldap::Capabilities + + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @strategy = GitHub::Ldap::MemberSearch::Detect.new(@ldap) + end + + def test_defers_to_configured_strategy + @ldap.configure_member_search_strategy(:classic) + + assert_kind_of GitHub::Ldap::MemberSearch::Classic, @strategy.strategy + end + + def test_detects_active_directory + caps = Net::LDAP::Entry.new + caps[:supportedcapabilities] = [ACTIVE_DIRECTORY_V61_R2_OID] + + @ldap.stub :capabilities, caps do + assert_kind_of GitHub::Ldap::MemberSearch::ActiveDirectory, + @strategy.strategy + end + end + + def test_falls_back_to_recursive + caps = Net::LDAP::Entry.new + caps[:supportedcapabilities] = [] + + @ldap.stub :capabilities, caps do + assert_kind_of GitHub::Ldap::MemberSearch::Recursive, + @strategy.strategy + end + end +end From 4f774fd7202cf4bf45bbe56f3412622073db99b9 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 16:00:41 -0800 Subject: [PATCH 127/307] Include Capabilities to reuse constant --- test/membership_validators/detect_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/membership_validators/detect_test.rb b/test/membership_validators/detect_test.rb index 8bf522a..34d476a 100644 --- a/test/membership_validators/detect_test.rb +++ b/test/membership_validators/detect_test.rb @@ -5,6 +5,8 @@ # and test against what AD *would* respond with. class GitHubLdapDetectMembershipValidatorsTest < GitHub::Ldap::Test + include GitHub::Ldap::Capabilities + def setup @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) @domain = @ldap.domain("dc=github,dc=com") @@ -26,8 +28,7 @@ def test_defers_to_configured_strategy def test_detects_active_directory caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = - [GitHub::Ldap::MembershipValidators::Detect::ACTIVE_DIRECTORY_V61_R2_OID] + caps[:supportedcapabilities] = [ACTIVE_DIRECTORY_V61_R2_OID] validator = make_validator(%w(group)) @ldap.stub :capabilities, caps do From d4d705daa4f02f721f8d2aab523cf6600d8cb613 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 17:32:01 -0800 Subject: [PATCH 128/307] Add ActiveDirectory member search strategy --- lib/github/ldap/member_search.rb | 6 +- .../ldap/member_search/active_directory.rb | 70 +++++++++++++++++ test/member_search/active_directory_test.rb | 77 +++++++++++++++++++ 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 lib/github/ldap/member_search/active_directory.rb create mode 100644 test/member_search/active_directory_test.rb diff --git a/lib/github/ldap/member_search.rb b/lib/github/ldap/member_search.rb index fc81607..b3965a5 100644 --- a/lib/github/ldap/member_search.rb +++ b/lib/github/ldap/member_search.rb @@ -1,6 +1,7 @@ require 'github/ldap/member_search/detect' require 'github/ldap/member_search/classic' require 'github/ldap/member_search/recursive' +require 'github/ldap/member_search/active_directory' module GitHub class Ldap @@ -15,8 +16,9 @@ class Ldap module MemberSearch # Internal: Mapping of strategy name to class. STRATEGIES = { - :classic => GitHub::Ldap::MemberSearch::Classic, - :recursive => GitHub::Ldap::MemberSearch::Recursive + :classic => GitHub::Ldap::MemberSearch::Classic, + :recursive => GitHub::Ldap::MemberSearch::Recursive, + :active_directory => GitHub::Ldap::MemberSearch::ActiveDirectory } end end diff --git a/lib/github/ldap/member_search/active_directory.rb b/lib/github/ldap/member_search/active_directory.rb new file mode 100644 index 0000000..6245012 --- /dev/null +++ b/lib/github/ldap/member_search/active_directory.rb @@ -0,0 +1,70 @@ +module GitHub + class Ldap + module MemberSearch + # Look up group members using the ActiveDirectory "in chain" matching rule. + # + # The 1.2.840.113556.1.4.1941 matching rule (LDAP_MATCHING_RULE_IN_CHAIN) + # "walks the chain of ancestry in objects all the way to the root until + # it finds a match". + # Source: http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx + # + # This means we have an efficient method of searching for group members, + # even in nested groups, performed on the server side. + class ActiveDirectory + OID = "1.2.840.113556.1.4.1941" + + # Internal: The default attributes to query for. + # NOTE: We technically don't need any by default, but if we left this + # empty, we'd be querying for *all* attributes which is less ideal. + DEFAULT_ATTRS = %w(objectClass) + + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Internal: The attributes to search for. + attr_reader :attrs + + # Public: Instantiate new search strategy. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options + def initialize(ldap, options = {}) + @ldap = ldap + @options = options + @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS + end + + # Public: Performs search for group members, including groups and + # members of subgroups, using ActiveDirectory's "in chain" matching + # rule. + # + # Returns Array of Net::LDAP::Entry objects. + def perform(group) + filter = member_of_in_chain_filter(group) + + # search for all members of the group, including subgroups, by + # searching "in chain". + domains.each_with_object([]) do |domain, members| + members.concat domain.search(filter: filter, attributes: attrs) + end + end + + # Internal: Constructs a member filter using the "in chain" + # extended matching rule afforded by ActiveDirectory. + # + # Returns a Net::LDAP::Filter object. + def member_of_in_chain_filter(entry) + Net::LDAP::Filter.ex("memberOf:#{OID}", entry.dn) + end + + # Internal: Domains to search through. + # + # Returns an Array of GitHub::Ldap::Domain objects. + def domains + @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } + end + private :domains + end + end + end +end diff --git a/test/member_search/active_directory_test.rb b/test/member_search/active_directory_test.rb new file mode 100644 index 0000000..e3f367a --- /dev/null +++ b/test/member_search/active_directory_test.rb @@ -0,0 +1,77 @@ +require_relative '../test_helper' + +class GitHubLdapActiveDirectoryMemberSearchStubbedTest < GitHub::Ldap::Test + # Only run when AD integration tests aren't run + def run(*) + self.class.test_env != "activedirectory" ? super : self + end + + def find_group(cn) + @domain.groups([cn]).first + end + + def setup + @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) + @domain = @ldap.domain("dc=github,dc=com") + @entry = @domain.user?('user1') + @strategy = GitHub::Ldap::MemberSearch::ActiveDirectory.new(@ldap) + end + + def test_finds_group_members + members = + @ldap.stub :search, [@entry] do + @strategy.perform(find_group("nested-group1")).map(&:dn) + end + assert_includes members, @entry.dn + end + + def test_finds_nested_group_members + members = + @ldap.stub :search, [@entry] do + @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn) + end + assert_includes members, @entry.dn + end + + def test_finds_deeply_nested_group_members + members = + @ldap.stub :search, [@entry] do + @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + end + assert_includes members, @entry.dn + end +end + +# See test/support/vm/activedirectory/README.md for details +class GitHubLdapActiveDirectoryMemberSearchIntegrationTest < GitHub::Ldap::Test + # Only run this test suite if ActiveDirectory is configured + def run(*) + self.class.test_env == "activedirectory" ? super : self + end + + def find_group(cn) + @domain.groups([cn]).first + end + + def setup + @ldap = GitHub::Ldap.new(options) + @domain = @ldap.domain(options[:search_domains]) + @entry = @domain.user?('user1') + @strategy = GitHub::Ldap::MemberSearch::ActiveDirectory.new(@ldap) + end + + def test_finds_group_members + members = @strategy.perform(find_group("nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn) + assert_includes members, @entry.dn + end + + def test_finds_deeply_nested_group_members + members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn) + assert_includes members, @entry.dn + end +end From a9b86ef83737943d16df960353169ad03d1b2da5 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 3 Dec 2014 17:41:21 -0800 Subject: [PATCH 129/307] Extract common behaviors for MemberSearch strategies --- lib/github/ldap/member_search.rb | 1 + .../ldap/member_search/active_directory.rb | 20 +++-------- lib/github/ldap/member_search/base.rb | 34 +++++++++++++++++++ lib/github/ldap/member_search/classic.rb | 14 +------- lib/github/ldap/member_search/recursive.rb | 22 ++++-------- 5 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 lib/github/ldap/member_search/base.rb diff --git a/lib/github/ldap/member_search.rb b/lib/github/ldap/member_search.rb index b3965a5..6269638 100644 --- a/lib/github/ldap/member_search.rb +++ b/lib/github/ldap/member_search.rb @@ -1,3 +1,4 @@ +require 'github/ldap/member_search/base' require 'github/ldap/member_search/detect' require 'github/ldap/member_search/classic' require 'github/ldap/member_search/recursive' diff --git a/lib/github/ldap/member_search/active_directory.rb b/lib/github/ldap/member_search/active_directory.rb index 6245012..f78085a 100644 --- a/lib/github/ldap/member_search/active_directory.rb +++ b/lib/github/ldap/member_search/active_directory.rb @@ -10,7 +10,7 @@ module MemberSearch # # This means we have an efficient method of searching for group members, # even in nested groups, performed on the server side. - class ActiveDirectory + class ActiveDirectory < Base OID = "1.2.840.113556.1.4.1941" # Internal: The default attributes to query for. @@ -18,9 +18,6 @@ class ActiveDirectory # empty, we'd be querying for *all* attributes which is less ideal. DEFAULT_ATTRS = %w(objectClass) - # Internal: The GitHub::Ldap object to search domains with. - attr_reader :ldap - # Internal: The attributes to search for. attr_reader :attrs @@ -28,10 +25,11 @@ class ActiveDirectory # # - ldap: GitHub::Ldap object # - options: Hash of options + # + # NOTE: This overrides default behavior to configure attrs`. def initialize(ldap, options = {}) - @ldap = ldap - @options = options - @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS + super + @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS end # Public: Performs search for group members, including groups and @@ -56,14 +54,6 @@ def perform(group) def member_of_in_chain_filter(entry) Net::LDAP::Filter.ex("memberOf:#{OID}", entry.dn) end - - # Internal: Domains to search through. - # - # Returns an Array of GitHub::Ldap::Domain objects. - def domains - @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } - end - private :domains end end end diff --git a/lib/github/ldap/member_search/base.rb b/lib/github/ldap/member_search/base.rb new file mode 100644 index 0000000..e3491f1 --- /dev/null +++ b/lib/github/ldap/member_search/base.rb @@ -0,0 +1,34 @@ +module GitHub + class Ldap + module MemberSearch + class Base + + # Internal: The GitHub::Ldap object to search domains with. + attr_reader :ldap + + # Public: Instantiate new search strategy. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options + def initialize(ldap, options = {}) + @ldap = ldap + @options = options + end + + # Public: Abstract: Performs search for group members. + # + # Returns Array of Net::LDAP::Entry objects. + # def perform(entry) + # end + + # Internal: Domains to search through. + # + # Returns an Array of GitHub::Ldap::Domain objects. + def domains + @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } + end + private :domains + end + end + end +end diff --git a/lib/github/ldap/member_search/classic.rb b/lib/github/ldap/member_search/classic.rb index 68a860b..47bb7a1 100644 --- a/lib/github/ldap/member_search/classic.rb +++ b/lib/github/ldap/member_search/classic.rb @@ -3,19 +3,7 @@ class Ldap module MemberSearch # Look up group members using the existing `Group#members` and # `Group#subgroups` API. - class Classic - # Internal: The GitHub::Ldap object to search domains with. - attr_reader :ldap - - # Public: Instantiate new search strategy. - # - # - ldap: GitHub::Ldap object - # - options: Hash of options (unused) - def initialize(ldap, options = {}) - @ldap = ldap - @options = options - end - + class Classic < Base # Public: Performs search for group members, including groups and # members of subgroups recursively. # diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 0df6a1c..5fe2489 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -5,15 +5,12 @@ module MemberSearch # # This results in a maximum of `depth` iterations/recursions to look up # members of a group and its subgroups. - class Recursive + class Recursive < Base include Filter DEFAULT_MAX_DEPTH = 9 DEFAULT_ATTRS = %w(member uniqueMember memberUid) - # Internal: The GitHub::Ldap object to search domains with. - attr_reader :ldap - # Internal: The maximum depth to search for members. attr_reader :depth @@ -24,11 +21,12 @@ class Recursive # # - ldap: GitHub::Ldap object # - options: Hash of options + # + # NOTE: This overrides default behavior to configure `depth` and `attrs`. def initialize(ldap, options = {}) - @ldap = ldap - @options = options - @depth = options[:depth] || DEFAULT_MAX_DEPTH - @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS + super + @depth = options[:depth] || DEFAULT_MAX_DEPTH + @attrs = Array(options[:attrs]).concat DEFAULT_ATTRS end # Public: Performs search for group members, including groups and @@ -129,14 +127,6 @@ def member_uids(entry) entry["memberUid"] end private :member_uids - - # Internal: Domains to search through. - # - # Returns an Array of GitHub::Ldap::Domain objects. - def domains - @domains ||= ldap.search_domains.map { |base| ldap.domain(base) } - end - private :domains end end end From 0e04583da578286a435b167f7d7676d69dd8dd07 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 13:59:53 -0800 Subject: [PATCH 130/307] Move requires to the top --- lib/github/ldap.rb | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index dc18a44..41d98dc 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -1,18 +1,19 @@ +require 'net/ldap' +require 'forwardable' + +require 'github/ldap/filter' +require 'github/ldap/domain' +require 'github/ldap/group' +require 'github/ldap/posix_group' +require 'github/ldap/virtual_group' +require 'github/ldap/virtual_attributes' +require 'github/ldap/instrumentation' +require 'github/ldap/capabilities' +require 'github/ldap/member_search' +require 'github/ldap/membership_validators' + module GitHub class Ldap - require 'net/ldap' - require 'forwardable' - require 'github/ldap/filter' - require 'github/ldap/domain' - require 'github/ldap/group' - require 'github/ldap/posix_group' - require 'github/ldap/virtual_group' - require 'github/ldap/virtual_attributes' - require 'github/ldap/instrumentation' - require 'github/ldap/capabilities' - require 'github/ldap/member_search' - require 'github/ldap/membership_validators' - include Instrumentation extend Forwardable From cb8f4918b364b1fa41e713d74a115ac791b4352f Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 14:57:49 -0800 Subject: [PATCH 131/307] Remove Detect meta-strategy, inline config/detection --- lib/github/ldap.rb | 72 +++++++++++++++---- lib/github/ldap/capabilities.rb | 24 ------- lib/github/ldap/member_search.rb | 22 ------ lib/github/ldap/member_search/detect.rb | 71 ------------------ lib/github/ldap/membership_validators.rb | 22 ------ .../ldap/membership_validators/detect.rb | 53 -------------- 6 files changed, 57 insertions(+), 207 deletions(-) delete mode 100644 lib/github/ldap/capabilities.rb delete mode 100644 lib/github/ldap/member_search/detect.rb delete mode 100644 lib/github/ldap/membership_validators/detect.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 41d98dc..d9c547c 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -18,6 +18,10 @@ class Ldap extend Forwardable + # Internal: The capability required to use ActiveDirectory features. + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze + # Utility method to get the last operation result with a human friendly message. # # Returns an OpenStruct with `code` and `message`. @@ -92,11 +96,8 @@ def initialize(options = {}) # when a base is not explicitly provided. @search_domains = Array(options[:search_domains]) - # configure which strategy should be used to validate user membership - configure_membership_validation_strategy(options[:membership_validator]) - - # configure which strategy should be used for member search - configure_member_search_strategy(options[:member_search_strategy]) + # configure both the membership validator and the member search strategies + configure_search_strategy(options[:search_strategy]) # enables instrumenting queries @instrumentation_service = options[:instrumentation_service] @@ -243,6 +244,19 @@ def configure_virtual_attributes(attributes) end end + # Internal: Configure the member search and membership validation strategies. + # + # TODO: Inline the logic in these two methods here. + # + # Returns nothing. + def configure_search_strategy(strategy = nil) + # configure which strategy should be used to validate user membership + configure_membership_validation_strategy(strategy) + + # configure which strategy should be used for member search + configure_member_search_strategy(strategy) + end + # Internal: Configure the membership validation strategy. # # Used by GitHub::Ldap::MembershipValidators::Detect to force a specific @@ -251,14 +265,23 @@ def configure_virtual_attributes(attributes) # If `strategy` is not provided, or doesn't match a known strategy, # defaults to `:detect`. Otherwise the configured strategy is selected. # - # Returns the selected membership validator strategy Symbol. + # Returns the membership validator strategy Class. def configure_membership_validation_strategy(strategy = nil) @membership_validator = case strategy.to_s - when "classic", "recursive", "active_directory" - strategy.to_sym + when "classic" + GitHub::Ldap::MembershipValidators::Classic + when "recursive" + GitHub::Ldap::MembershipValidators::Recursive + when "active_directory" + GitHub::Ldap::MembershipValidators::ActiveDirectory else - :detect + # fallback to detection, defaulting to recursive strategy + if active_directory_capability? + GitHub::Ldap::MembershipValidators::ActiveDirectory + else + GitHub::Ldap::MembershipValidators::Recursive + end end end @@ -273,12 +296,31 @@ def configure_membership_validation_strategy(strategy = nil) # Returns the selected strategy Symbol. def configure_member_search_strategy(strategy = nil) @member_search_strategy = - case strategy.to_s - when "classic", "recursive" - strategy.to_sym - else - :detect - end + case strategy.to_s + when "classic" + GitHub::Ldap::MemberSearch::Classic + when "recursive" + GitHub::Ldap::MemberSearch::Recursive + when "active_directory" + GitHub::Ldap::MemberSearch::ActiveDirectory + else + # fallback to detection, defaulting to recursive strategy + if active_directory_capability? + GitHub::Ldap::MemberSearch::ActiveDirectory + else + GitHub::Ldap::MemberSearch::Recursive + end + end + end + + # Internal: Detect whether the LDAP host is an ActiveDirectory server. + # + # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + # + # Returns true if the host is an ActiveDirectory server, false otherwise. + def active_directory_capability? + capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) end + private :active_directory_capability? end end diff --git a/lib/github/ldap/capabilities.rb b/lib/github/ldap/capabilities.rb deleted file mode 100644 index 370be4b..0000000 --- a/lib/github/ldap/capabilities.rb +++ /dev/null @@ -1,24 +0,0 @@ -module GitHub - class Ldap - module Capabilities - # Internal: The capability required to use the ActiveDirectory strategy. - # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze - - # Internal: Detect whether the LDAP host is an ActiveDirectory server. - # - # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - # - # Returns true if the host is an ActiveDirectory server, false otherwise. - def active_directory_capability? - capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) - end - - # Internal: Returns the Net::LDAP::Entry object describing the LDAP - # host's capabilities (via the Root DSE). - def capabilities - ldap.capabilities - end - end - end -end diff --git a/lib/github/ldap/member_search.rb b/lib/github/ldap/member_search.rb index 6269638..d051268 100644 --- a/lib/github/ldap/member_search.rb +++ b/lib/github/ldap/member_search.rb @@ -1,26 +1,4 @@ require 'github/ldap/member_search/base' -require 'github/ldap/member_search/detect' require 'github/ldap/member_search/classic' require 'github/ldap/member_search/recursive' require 'github/ldap/member_search/active_directory' - -module GitHub - class Ldap - # Provides various strategies for member lookup. - # - # For example: - # - # group = domain.groups(%w(Engineering)).first - # strategy = GitHub::Ldap::MemberSearch::Recursive.new(ldap) - # strategy.perform(group) #=> [#] - # - module MemberSearch - # Internal: Mapping of strategy name to class. - STRATEGIES = { - :classic => GitHub::Ldap::MemberSearch::Classic, - :recursive => GitHub::Ldap::MemberSearch::Recursive, - :active_directory => GitHub::Ldap::MemberSearch::ActiveDirectory - } - end - end -end diff --git a/lib/github/ldap/member_search/detect.rb b/lib/github/ldap/member_search/detect.rb deleted file mode 100644 index 06cdb32..0000000 --- a/lib/github/ldap/member_search/detect.rb +++ /dev/null @@ -1,71 +0,0 @@ -module GitHub - class Ldap - module MemberSearch - # Detects the LDAP host's capabilities and determines the appropriate - # member search strategy at runtime. - # - # Currently detects for ActiveDirectory in-chain membership validation. - # - # An explicit strategy can also be defined via - # `GitHub::Ldap#member_search_strategy=`. - # - # See also `GitHub::Ldap#configure_member_search_strategy`. - class Detect - # Defines `active_directory_capability?` and necessary helpers. - include GitHub::Ldap::Capabilities - - # Internal: The GitHub::Ldap object to search domains with. - attr_reader :ldap - - # Internal: The Hash of options to pass through to the strategy. - attr_reader :options - - # Public: Instantiate a meta strategy to detect the right strategy - # to use for the search, and call that strategy, at runtime. - # - # - ldap: GitHub::Ldap object - # - options: Hash of options (passed through) - def initialize(ldap, options = {}) - @ldap = ldap - @options = options - end - - # Public: Performs search for group members via the appropriate search - # strategy detected/configured. - # - # Returns Array of Net::LDAP::Entry objects. - def perform(entry) - strategy.perform(entry) - end - - # Internal: Returns the member search strategy object. - def strategy - @strategy ||= begin - strategy = detect_strategy - strategy.new(ldap, options) - end - end - - # Internal: Find the most appropriate search strategy, either by - # configuration or by detecting the host's capabilities. - # - # Returns the strategy class. - def detect_strategy - case - when GitHub::Ldap::MemberSearch::STRATEGIES.key?(strategy_config) - GitHub::Ldap::MemberSearch::STRATEGIES[strategy_config] - when active_directory_capability? - GitHub::Ldap::MemberSearch::STRATEGIES[:active_directory] - else - GitHub::Ldap::MemberSearch::STRATEGIES[:recursive] - end - end - - # Internal: Returns the configured member search strategy Symbol. - def strategy_config - ldap.member_search_strategy - end - end - end - end -end diff --git a/lib/github/ldap/membership_validators.rb b/lib/github/ldap/membership_validators.rb index 1135a2d..c629a37 100644 --- a/lib/github/ldap/membership_validators.rb +++ b/lib/github/ldap/membership_validators.rb @@ -1,26 +1,4 @@ require 'github/ldap/membership_validators/base' -require 'github/ldap/membership_validators/detect' require 'github/ldap/membership_validators/classic' require 'github/ldap/membership_validators/recursive' require 'github/ldap/membership_validators/active_directory' - -module GitHub - class Ldap - # Provides various strategies for validating membership. - # - # For example: - # - # groups = domain.groups(%w(Engineering)) - # validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, groups) - # validator.perform(entry) #=> true - # - module MembershipValidators - # Internal: Mapping of strategy name to class. - STRATEGIES = { - :classic => GitHub::Ldap::MembershipValidators::Classic, - :recursive => GitHub::Ldap::MembershipValidators::Recursive, - :active_directory => GitHub::Ldap::MembershipValidators::ActiveDirectory - } - end - end -end diff --git a/lib/github/ldap/membership_validators/detect.rb b/lib/github/ldap/membership_validators/detect.rb deleted file mode 100644 index 74f7f0d..0000000 --- a/lib/github/ldap/membership_validators/detect.rb +++ /dev/null @@ -1,53 +0,0 @@ -module GitHub - class Ldap - module MembershipValidators - # Detects the LDAP host's capabilities and determines the appropriate - # membership validation strategy at runtime. Currently detects for - # ActiveDirectory in-chain membership validation. An explicit strategy can - # also be defined via `GitHub::Ldap#membership_validator=`. See also - # `GitHub::Ldap#configure_membership_validation_strategy`. - class Detect < Base - # Defines `active_directory_capability?` and necessary helpers. - include GitHub::Ldap::Capabilities - - def perform(entry) - # short circuit validation if there are no groups to check against - return true if groups.empty? - - strategy.perform(entry) - end - - # Internal: Returns the membership validation strategy object. - def strategy - @strategy ||= begin - strategy = detect_strategy - strategy.new(ldap, groups) - end - end - - # Internal: Detects LDAP host's capabilities and chooses the best - # strategy for the host. - # - # If the strategy has been set explicitly, skips detection and uses the - # configured strategy instead. - # - # Returns the strategy class. - def detect_strategy - case - when GitHub::Ldap::MembershipValidators::STRATEGIES.key?(strategy_config) - GitHub::Ldap::MembershipValidators::STRATEGIES[strategy_config] - when active_directory_capability? - GitHub::Ldap::MembershipValidators::STRATEGIES[:active_directory] - else - GitHub::Ldap::MembershipValidators::STRATEGIES[:recursive] - end - end - - # Internal: Returns the configured membership validator strategy Symbol. - def strategy_config - ldap.membership_validator - end - end - end - end -end From d7b83cd312ce817ccb2c6a3aa5fd650085415e55 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 15:01:32 -0800 Subject: [PATCH 132/307] Capabilities module was removed --- lib/github/ldap.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index d9c547c..8c46cd1 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -8,7 +8,6 @@ require 'github/ldap/virtual_group' require 'github/ldap/virtual_attributes' require 'github/ldap/instrumentation' -require 'github/ldap/capabilities' require 'github/ldap/member_search' require 'github/ldap/membership_validators' From 42ffdb659cdff72d6e61817ac93ecb62e78359b8 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 18:45:22 -0800 Subject: [PATCH 133/307] Test search strategy config, detection, defaults --- test/ldap_test.rb | 45 +++++++++++++------- test/member_search/detect_test.rb | 42 ------------------- test/membership_validators/detect_test.rb | 50 ----------------------- 3 files changed, 31 insertions(+), 106 deletions(-) delete mode 100644 test/member_search/detect_test.rb delete mode 100644 test/membership_validators/detect_test.rb diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 6d4d5fb..48511d8 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -73,28 +73,45 @@ def test_instruments_search assert_equal "dc=github,dc=com", payload[:base] end - def test_membership_validator_default - assert_equal :detect, @ldap.membership_validator + def test_search_strategy_defaults + assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end - def test_membership_validator_configured_to_classic_strategy - @ldap.configure_membership_validation_strategy :classic - assert_equal :classic, @ldap.membership_validator + def test_search_strategy_detects_active_directory + caps = Net::LDAP::Entry.new + caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V61_R2_OID] + + @ldap.stub :capabilities, caps do + @ldap.configure_search_strategy :detect + + assert_equal GitHub::Ldap::MembershipValidators::ActiveDirectory, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::ActiveDirectory, @ldap.member_search_strategy + end + end + + def test_search_strategy_configured_to_classic + @ldap.configure_search_strategy :classic + assert_equal GitHub::Ldap::MembershipValidators::Classic, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::Classic, @ldap.member_search_strategy end - def test_membership_validator_configured_to_recursive_strategy - @ldap.configure_membership_validation_strategy :recursive - assert_equal :recursive, @ldap.membership_validator + def test_search_strategy_configured_to_recursive + @ldap.configure_search_strategy :recursive + assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end - def test_membership_validator_configured_to_active_directory_strategy - @ldap.configure_membership_validation_strategy :active_directory - assert_equal :active_directory, @ldap.membership_validator + def test_search_strategy_configured_to_active_directory + @ldap.configure_search_strategy :active_directory + assert_equal GitHub::Ldap::MembershipValidators::ActiveDirectory, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::ActiveDirectory, @ldap.member_search_strategy end - def test_membership_validator_misconfigured_to_unrecognized_strategy_falls_back_to_default - @ldap.configure_membership_validation_strategy :unknown - assert_equal :detect, @ldap.membership_validator + def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_default + @ldap.configure_search_strategy :unknown + assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator + assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end def test_capabilities diff --git a/test/member_search/detect_test.rb b/test/member_search/detect_test.rb deleted file mode 100644 index 6092cdf..0000000 --- a/test/member_search/detect_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -require_relative '../test_helper' - -# NOTE: Since this strategy is targeted at detecting ActiveDirectory -# capabilities, and we don't have AD setup in CI, we stub out actual queries -# and test against what AD *would* respond with. - -class GitHubLdapDetectMemberSearchTest < GitHub::Ldap::Test - include GitHub::Ldap::Capabilities - - def setup - @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1') - @strategy = GitHub::Ldap::MemberSearch::Detect.new(@ldap) - end - - def test_defers_to_configured_strategy - @ldap.configure_member_search_strategy(:classic) - - assert_kind_of GitHub::Ldap::MemberSearch::Classic, @strategy.strategy - end - - def test_detects_active_directory - caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [ACTIVE_DIRECTORY_V61_R2_OID] - - @ldap.stub :capabilities, caps do - assert_kind_of GitHub::Ldap::MemberSearch::ActiveDirectory, - @strategy.strategy - end - end - - def test_falls_back_to_recursive - caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [] - - @ldap.stub :capabilities, caps do - assert_kind_of GitHub::Ldap::MemberSearch::Recursive, - @strategy.strategy - end - end -end diff --git a/test/membership_validators/detect_test.rb b/test/membership_validators/detect_test.rb deleted file mode 100644 index 34d476a..0000000 --- a/test/membership_validators/detect_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -require_relative '../test_helper' - -# NOTE: Since this strategy is targeted at detecting ActiveDirectory -# capabilities, and we don't have AD setup in CI, we stub out actual queries -# and test against what AD *would* respond with. - -class GitHubLdapDetectMembershipValidatorsTest < GitHub::Ldap::Test - include GitHub::Ldap::Capabilities - - def setup - @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com))) - @domain = @ldap.domain("dc=github,dc=com") - @entry = @domain.user?('user1') - @validator = GitHub::Ldap::MembershipValidators::Detect - end - - def make_validator(groups) - groups = @domain.groups(groups) - @validator.new(@ldap, groups) - end - - def test_defers_to_configured_strategy - @ldap.configure_membership_validation_strategy(:classic) - validator = make_validator(%w(group)) - - assert_kind_of GitHub::Ldap::MembershipValidators::Classic, validator.strategy - end - - def test_detects_active_directory - caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [ACTIVE_DIRECTORY_V61_R2_OID] - - validator = make_validator(%w(group)) - @ldap.stub :capabilities, caps do - assert_kind_of GitHub::Ldap::MembershipValidators::ActiveDirectory, - validator.strategy - end - end - - def test_falls_back_to_recursive - caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [] - - validator = make_validator(%w(group)) - @ldap.stub :capabilities, caps do - assert_kind_of GitHub::Ldap::MembershipValidators::Recursive, - validator.strategy - end - end -end From a62ca6c716ab73e5e6dae96bf21d02bc1e289bba Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 19:00:31 -0800 Subject: [PATCH 134/307] Fix config method doco --- lib/github/ldap.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 8c46cd1..ebdfddc 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -258,11 +258,8 @@ def configure_search_strategy(strategy = nil) # Internal: Configure the membership validation strategy. # - # Used by GitHub::Ldap::MembershipValidators::Detect to force a specific - # strategy (instead of detecting host capabilities and deciding at runtime). - # - # If `strategy` is not provided, or doesn't match a known strategy, - # defaults to `:detect`. Otherwise the configured strategy is selected. + # If no known strategy is provided, detects ActiveDirectory capabilities or + # falls back to the Recursive strategy by default. # # Returns the membership validator strategy Class. def configure_membership_validation_strategy(strategy = nil) @@ -286,13 +283,11 @@ def configure_membership_validation_strategy(strategy = nil) # Internal: Configure the member search strategy. # - # Used by GitHub::Ldap::MemberSearch::Detect to force a specific strategy - # (instead of detecting the host capabilities and deciding at runtime). # - # If `strategy` is not provided, or doesn't match a known strategy, - # defaults to `:detect`. Otherwise the configured strategy is selected. + # If no known strategy is provided, detects ActiveDirectory capabilities or + # falls back to the Recursive strategy by default. # - # Returns the selected strategy Symbol. + # Returns the selected strategy Class. def configure_member_search_strategy(strategy = nil) @member_search_strategy = case strategy.to_s From 310a531f2745de6045a26bbcd8efeb874c90b7c4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 19:03:01 -0800 Subject: [PATCH 135/307] Bump Gemspec version, add author, fix capitalization --- github-ldap.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 0033761..5b526de 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,11 +2,11 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.5.0" - spec.authors = ["David Calavera"] - spec.email = ["david.calavera@gmail.com"] - spec.description = %q{Ldap authentication for humans} - spec.summary = %q{Ldap client authentication wrapper without all the boilerplate} + spec.version = "1.6.0" + spec.authors = ["David Calavera", "Matt Todd"] + spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] + spec.description = %q{LDAP authentication for humans} + spec.summary = %q{LDAP client authentication wrapper without all the boilerplate} spec.homepage = "https://github.com/github/github-ldap" spec.license = "MIT" From 7f6f18056069ab0b78107bed13a2aca433e0525e Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 19:07:31 -0800 Subject: [PATCH 136/307] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c108b5..38f8020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v1.6.0 + +* Expose `GitHub::Ldap::Group.group?` for testing if entry is a group [#67](https://github.com/github/github-ldap/pull/67) +* Add *Member Search* strategies [#64](https://github.com/github/github-ldap/pull/64) [#68](https://github.com/github/github-ldap/pull/68) [#69](https://github.com/github/github-ldap/pull/69) +* Simplify *Member Search* and *Membership Validation* search strategy configuration, detection, and default behavior [#70](https://github.com/github/github-ldap/pull/70) + ## v1.5.0 * Automatically detect membership validator strategy by default [#58](https://github.com/github/github-ldap/pull/58) [#62](https://github.com/github/github-ldap/pull/62) From d0ae894c2e6aea7f754d895127e843ccdcba8d77 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Dec 2014 19:41:47 -0800 Subject: [PATCH 137/307] Release 1.6.0 From 37f3510ef55fbdc8bc07833bc22e5a49219cc371 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 9 Dec 2014 17:15:45 -0800 Subject: [PATCH 138/307] Require net-ldap 0.10.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 5b526de..35fd1ba 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.9.0' + spec.add_dependency 'net-ldap', '~> 0.10.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From 9b820ee975ef57aaec8efd0551c7b64639af9745 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 9 Dec 2014 17:25:26 -0800 Subject: [PATCH 139/307] Accept depth as option for Recursive strategy constructor This makes the perform method signature identical for all strategies. --- lib/github/ldap/membership_validators/base.rb | 8 +++++--- .../ldap/membership_validators/recursive.rb | 17 ++++++++++++++++- test/membership_validators/recursive_test.rb | 8 ++++---- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/github/ldap/membership_validators/base.rb b/lib/github/ldap/membership_validators/base.rb index 3c47853..be378d1 100644 --- a/lib/github/ldap/membership_validators/base.rb +++ b/lib/github/ldap/membership_validators/base.rb @@ -13,9 +13,11 @@ class Base # # - ldap: GitHub::Ldap object # - groups: Array of Net::LDAP::Entry group objects - def initialize(ldap, groups) - @ldap = ldap - @groups = groups + # - options: Hash of options + def initialize(ldap, groups, options = {}) + @ldap = ldap + @groups = groups + @options = options end # Abstract: Performs the membership validation check. diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 8c40aeb..8544f30 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -21,7 +21,22 @@ class Recursive < Base DEFAULT_MAX_DEPTH = 9 ATTRS = %w(dn cn) - def perform(entry, depth = DEFAULT_MAX_DEPTH) + # Internal: The maximum depth to search for membership. + attr_reader :depth + + # Public: Instantiate new search strategy. + # + # - ldap: GitHub::Ldap object + # - options: Hash of options + # - groups: Array of Net::LDAP::Entry group objects + # + # NOTE: This overrides default behavior to configure `depth`. + def initialize(ldap, groups, options = {}) + super + @depth = options[:depth] || DEFAULT_MAX_DEPTH + end + + def perform(entry) # short circuit validation if there are no groups to check against return true if groups.empty? diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb index e351532..ea6462d 100644 --- a/test/membership_validators/recursive_test.rb +++ b/test/membership_validators/recursive_test.rb @@ -8,9 +8,9 @@ def setup @validator = GitHub::Ldap::MembershipValidators::Recursive end - def make_validator(groups) + def make_validator(groups, depth = nil) groups = @domain.groups(groups) - @validator.new(@ldap, groups) + @validator.new(@ldap, groups, depth: depth) end def test_validates_user_in_group @@ -34,8 +34,8 @@ def test_validates_user_in_great_grandchild_group end def test_does_not_validate_user_in_great_granchild_group_with_depth - validator = make_validator(%w(n-depth-nested-group3)) - refute validator.perform(@entry, 2) + validator = make_validator(%w(n-depth-nested-group3), 2) + refute validator.perform(@entry) end def test_does_not_validate_user_not_in_group From 4c2dc84e87fd3e1a1849414fe0eb69280fb4781d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 9 Dec 2014 17:27:26 -0800 Subject: [PATCH 140/307] Minor tweak to test helper --- test/membership_validators/recursive_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb index ea6462d..072ffca 100644 --- a/test/membership_validators/recursive_test.rb +++ b/test/membership_validators/recursive_test.rb @@ -8,9 +8,9 @@ def setup @validator = GitHub::Ldap::MembershipValidators::Recursive end - def make_validator(groups, depth = nil) + def make_validator(groups, options = {}) groups = @domain.groups(groups) - @validator.new(@ldap, groups, depth: depth) + @validator.new(@ldap, groups, options) end def test_validates_user_in_group @@ -34,7 +34,7 @@ def test_validates_user_in_great_grandchild_group end def test_does_not_validate_user_in_great_granchild_group_with_depth - validator = make_validator(%w(n-depth-nested-group3), 2) + validator = make_validator(%w(n-depth-nested-group3), depth: 2) refute validator.perform(@entry) end From ec8ee6cfbc5a588c5defd66f611313d6f4a6e7b4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 10 Dec 2014 23:34:36 -0800 Subject: [PATCH 141/307] Document depth option --- lib/github/ldap/membership_validators/recursive.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 8544f30..4567261 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -27,8 +27,9 @@ class Recursive < Base # Public: Instantiate new search strategy. # # - ldap: GitHub::Ldap object + # - groups: Array of Net::LDAP::Entry group objects # - options: Hash of options - # - groups: Array of Net::LDAP::Entry group objects + # depth: Integer limit of recursion # # NOTE: This overrides default behavior to configure `depth`. def initialize(ldap, groups, options = {}) From d72403679e2429be3b537f57a884cc9c2c401939 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 10 Dec 2014 23:35:03 -0800 Subject: [PATCH 142/307] Keep depth param as override --- lib/github/ldap/membership_validators/recursive.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index 4567261..ff78f5a 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -37,7 +37,7 @@ def initialize(ldap, groups, options = {}) @depth = options[:depth] || DEFAULT_MAX_DEPTH end - def perform(entry) + def perform(entry, depth_override = nil) # short circuit validation if there are no groups to check against return true if groups.empty? @@ -52,7 +52,7 @@ def perform(entry) next if membership.empty? # recurse to at most `depth` - depth.times do |n| + (depth_override || depth).times do |n| # find groups whose members include membership groups membership = domain.search(filter: membership_filter(membership), attributes: ATTRS) From 51e2732112a3b61305b08ea4486d9ba403e6aec5 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 11 Dec 2014 00:01:46 -0800 Subject: [PATCH 143/307] Warn that the depth_override param is deprecated cc @jch --- lib/github/ldap/membership_validators/recursive.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/github/ldap/membership_validators/recursive.rb b/lib/github/ldap/membership_validators/recursive.rb index ff78f5a..3b78545 100644 --- a/lib/github/ldap/membership_validators/recursive.rb +++ b/lib/github/ldap/membership_validators/recursive.rb @@ -38,6 +38,14 @@ def initialize(ldap, groups, options = {}) end def perform(entry, depth_override = nil) + if depth_override + warn "DEPRECATION WARNING: Calling Recursive#perform with a second argument is deprecated." + warn "Usage:" + warn " strategy = GitHub::Ldap::MembershipValidators::Recursive.new \\" + warn " ldap, depth: 5" + warn " strategy#perform(entry)" + end + # short circuit validation if there are no groups to check against return true if groups.empty? From f9a8cc253e4e22814b1a9ee97063cf57cfa193d5 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 11 Dec 2014 00:29:15 -0800 Subject: [PATCH 144/307] Bump gemspec version --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 35fd1ba..3383960 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.6.0" + spec.version = "1.7.0" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From 1ef910c91ef5a4f25c56772771297515cf2f999a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 11 Dec 2014 00:33:04 -0800 Subject: [PATCH 145/307] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f8020..4034f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v1.7.0 + +* Accept `:depth` option for Recursive membership validator strategy instance [#73](https://github.com/github/github-ldap/pull/73) +* Deprecate `depth` argument to `Recursive` membership validator `perform` method +* Bump net-ldap dependency to 0.10.0 at minimum [#72](https://github.com/github/github-ldap/pull/72) + ## v1.6.0 * Expose `GitHub::Ldap::Group.group?` for testing if entry is a group [#67](https://github.com/github/github-ldap/pull/67) From db53a058c90f74adaea32ae2b5902e359d76710a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 11 Dec 2014 13:17:39 -0800 Subject: [PATCH 146/307] Release 1.7.0 From 55b554e48ad0f0fd50ccede0246891bbde973b14 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 23 Dec 2014 14:34:17 -0800 Subject: [PATCH 147/307] add ad group filter --- lib/github/ldap/filter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/filter.rb b/lib/github/ldap/filter.rb index 64f5aa3..6f62af3 100644 --- a/lib/github/ldap/filter.rb +++ b/lib/github/ldap/filter.rb @@ -3,7 +3,8 @@ class Ldap module Filter ALL_GROUPS_FILTER = Net::LDAP::Filter.eq("objectClass", "groupOfNames") | Net::LDAP::Filter.eq("objectClass", "groupOfUniqueNames") | - Net::LDAP::Filter.eq("objectClass", "posixGroup") + Net::LDAP::Filter.eq("objectClass", "posixGroup") | + Net::LDAP::Filter.eq("objectClass", "group") MEMBERSHIP_NAMES = %w(member uniqueMember) From 8446d9d8d2e65dfa9ce001dea8deb5cef4bf6a89 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 23 Dec 2014 15:58:20 -0800 Subject: [PATCH 148/307] release 1.7.1 --- CHANGELOG.md | 4 ++++ github-ldap.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4034f45..b72dbe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.7.1 + +* Add Active Directory group filter [#75](https://github.com/github/github-ldap/pull/75) + ## v1.7.0 * Accept `:depth` option for Recursive membership validator strategy instance [#73](https://github.com/github/github-ldap/pull/73) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 3383960..3ccbd4e 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.7.0" + spec.version = "1.7.1" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From 9daf749ae74ac81bafc019ae6a15aebf6865aa13 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Tue, 23 Dec 2014 16:04:48 -0800 Subject: [PATCH 149/307] Release 1.7.1 --- test/domain_test.rb | 12 ++++++++++++ test/filter_test.rb | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/test/domain_test.rb b/test/domain_test.rb index 797f716..446a6e0 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -224,3 +224,15 @@ def test_membership_for_posixGroups "Expected `#{@cn}` to not include the member `#{user.dn}`" end end + +class GitHubLdapActiveDirectoryGroupsTest < GitHub::Ldap::Test + def run(*) + self.class.test_env != "activedirectory" ? super : self + end + + def test_filter_groups + domain = @ldap.domain("DC=ad,DC=ghe,DC=local") + results = domain.filter_groups("ghe-admins") + assert_equal 1, results.size + end +end diff --git a/test/filter_test.rb b/test/filter_test.rb index 58992a8..9d8160c 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -78,4 +78,8 @@ def test_all_members_by_uid assert_equal "(|(uid=calavera)(uid=mtodd))", @subject.all_members_by_uid(%w(calavera mtodd), :uid).to_s end + + def test_active_directory_group + + end end From a5b2a0060eeefd10ee89a1f9cae9983efb56b82a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 16:04:28 -0800 Subject: [PATCH 150/307] Fix domain test --- test/domain_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 446a6e0..35a6bff 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -231,7 +231,7 @@ def run(*) end def test_filter_groups - domain = @ldap.domain("DC=ad,DC=ghe,DC=local") + domain = GitHub::Ldap.new(options).domain("DC=ad,DC=ghe,DC=local") results = domain.filter_groups("ghe-admins") assert_equal 1, results.size end From 7bc49bc5f468edbc5c89e815130ddac4356e4452 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 16:04:39 -0800 Subject: [PATCH 151/307] Remove unnecessary AD group filter test --- test/filter_test.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/filter_test.rb b/test/filter_test.rb index 9d8160c..58992a8 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -78,8 +78,4 @@ def test_all_members_by_uid assert_equal "(|(uid=calavera)(uid=mtodd))", @subject.all_members_by_uid(%w(calavera mtodd), :uid).to_s end - - def test_active_directory_group - - end end From 0d9b7978c2fe9a0b5c76bc710563237a1a34d1d1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 16:07:46 -0800 Subject: [PATCH 152/307] Fix AD-specific runner guard --- test/domain_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 35a6bff..4480e60 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -227,7 +227,7 @@ def test_membership_for_posixGroups class GitHubLdapActiveDirectoryGroupsTest < GitHub::Ldap::Test def run(*) - self.class.test_env != "activedirectory" ? super : self + self.class.test_env == "activedirectory" ? super : self end def test_filter_groups From 2f49d6b494ae8e9b5f951d68f611ca25ff87e8f2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 17:00:32 -0800 Subject: [PATCH 153/307] :fire: unnused variables --- lib/github/ldap/member_search/recursive.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5fe2489..b1af919 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -49,11 +49,6 @@ def perform(group) depth.times do |n| # find every (new, unique) member entry depth_subentries = entries.each_with_object([]) do |entry, depth_entries| - submembers = entry["member"] - - # skip any members we've already found - submembers.reject! { |dn| found.key?(dn) } - # find members of subgroup, including subgroups (N queries) subentries = member_entries(entry) next if subentries.empty? From 212816c8407d5a72ea332450a56919c5d91a3546 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 17:00:54 -0800 Subject: [PATCH 154/307] Skip searching for entries already found --- lib/github/ldap/member_search/recursive.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index b1af919..f75ebfc 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -50,7 +50,7 @@ def perform(group) # find every (new, unique) member entry depth_subentries = entries.each_with_object([]) do |entry, depth_entries| # find members of subgroup, including subgroups (N queries) - subentries = member_entries(entry) + subentries = member_entries(entry, found) next if subentries.empty? # track found subentries @@ -75,11 +75,15 @@ def perform(group) # entry. # # Returns an Array of Net::LDAP::Entry objects. - def member_entries(entry) + def member_entries(entry, found = {}) entries = [] dns = member_dns(entry) uids = member_uids(entry) + # skip any entries we've already found + dns.reject! { |dn| found.key?(dn) } + uids.reject! { |uid| found.any? { |entry| entry['uid'].include?(uid) } } + entries.concat entries_by_uid(uids) unless uids.empty? entries.concat entries_by_dn(dns) unless dns.empty? From 2a62c2112def6afd865e7a161955a082a5de492b Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 21:54:11 -0800 Subject: [PATCH 155/307] Find subgroups, then materialize members --- lib/github/ldap/member_search/recursive.rb | 104 ++++++++++----------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index f75ebfc..8eaa2e6 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -34,72 +34,70 @@ def initialize(ldap, options = {}) # # Returns Array of Net::LDAP::Entry objects. def perform(group) - found = Hash.new - - # find members (N queries) - entries = member_entries(group) - return [] if entries.empty? - - # track found entries - entries.each do |entry| - found[entry.dn] = entry + found = Hash.new + searched = [] + entries = [] + + # if this is a posixGroup, return members immediately (no nesting) + uids = member_uids(group) + return entries_by_uid(uids) if uids.any? + + # track group + searched << group.dn + found[group.dn] = group + + # pull out base group's member DNs + dns = member_dns(group) + + # search for base group's subgroups + filter = ALL_GROUPS_FILTER + groups = dns.each_with_object([]) do |dn, groups| + groups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + searched << dn end - # descend to `depth` levels, at most - depth.times do |n| - # find every (new, unique) member entry - depth_subentries = entries.each_with_object([]) do |entry, depth_entries| - # find members of subgroup, including subgroups (N queries) - subentries = member_entries(entry, found) - next if subentries.empty? + # track found groups + groups.each { |g| found[g.dn] = g } - # track found subentries - subentries.each { |entry| found[entry.dn] = entry } + # recursively find subgroups + unless groups.empty? + depth.times do |n| + # pull out subgroups' member DNs to search through + sub_dns = groups.each_with_object([]) do |subgroup, sub_dns| + sub_dns.concat member_dns(subgroup) + end - # collect all entries for this depth - depth_entries.concat subentries - end + # give up if there's nothing else to search for + break if sub_dns.empty? - # stop if there are no more subgroups to search - break if depth_subentries.empty? + # filter out if already searched for + sub_dns.reject! { |dn| searched.include?(dn) } - # go one level deeper - entries = depth_subentries - end + # search for subgroups + subgroups = sub_dns.each_with_object([]) do |dn, subgroups| + subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + searched << dn + end - # return all found entries - found.values - end + break if subgroups.empty? - # Internal: Fetch member entries, including subgroups, for the given - # entry. - # - # Returns an Array of Net::LDAP::Entry objects. - def member_entries(entry, found = {}) - entries = [] - dns = member_dns(entry) - uids = member_uids(entry) + # track found groups + subgroups.each { |g| found[g.dn] = g } - # skip any entries we've already found - dns.reject! { |dn| found.key?(dn) } - uids.reject! { |uid| found.any? { |entry| entry['uid'].include?(uid) } } + # descend another level + groups = subgroups + end + end - entries.concat entries_by_uid(uids) unless uids.empty? - entries.concat entries_by_dn(dns) unless dns.empty? + # take found groups and combine groups and members into list of entries + found.values.each do |group| + entries << group + # just need member DNs as Net::LDAP::Entry objects + entries.concat member_dns(group).map { |dn| Net::LDAP::Entry.new(dn) } + end entries end - private :member_entries - - # Internal: Bind a list of DNs to their respective entries. - # - # Returns an Array of Net::LDAP::Entry objects. - def entries_by_dn(members) - members.map do |dn| - ldap.domain(dn).bind(attributes: attrs) - end.compact - end - private :entries_by_dn # Internal: Fetch entries by UID. # From cb4bd2ce9f2d18ef627c05f63b2f8105586fcf5c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 22:05:24 -0800 Subject: [PATCH 156/307] Ensure unique entries --- lib/github/ldap/member_search/recursive.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 8eaa2e6..ab1374c 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -89,12 +89,14 @@ def perform(group) end end - # take found groups and combine groups and members into list of entries - found.values.each do |group| + # pull member DNs, discarding dupes and subgroup DNs + member_dns = found.values.each_with_object([]) do |group, member_dns| entries << group - # just need member DNs as Net::LDAP::Entry objects - entries.concat member_dns(group).map { |dn| Net::LDAP::Entry.new(dn) } - end + member_dns.concat member_dns(group) + end.uniq.reject { |dn| found.key?(dn) } + + # wrap member DNs in Net::LDAP::Entry objects + entries.concat member_dns.map { |dn| Net::LDAP::Entry.new(dn) } entries end From 909da53b87a6c7e304fbb97d1afd0747aab7a959 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:37:50 -0800 Subject: [PATCH 157/307] Doc, reorder variables to make purpose explicit --- lib/github/ldap/member_search/recursive.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index ab1374c..9b04df3 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -34,9 +34,11 @@ def initialize(ldap, options = {}) # # Returns Array of Net::LDAP::Entry objects. def perform(group) - found = Hash.new + # track groups found + found = Hash.new + + # track DNs searched for (so we don't repeat searches) searched = [] - entries = [] # if this is a posixGroup, return members immediately (no nesting) uids = member_uids(group) @@ -89,6 +91,9 @@ def perform(group) end end + # entries to return + entries = [] + # pull member DNs, discarding dupes and subgroup DNs member_dns = found.values.each_with_object([]) do |group, member_dns| entries << group From 09e05a67d030739a78b690afe3d1a3576869fade Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:41:11 -0800 Subject: [PATCH 158/307] Use a Set to track DNs searched for --- lib/github/ldap/member_search/recursive.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 9b04df3..fa3d5a8 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -37,8 +37,8 @@ def perform(group) # track groups found found = Hash.new - # track DNs searched for (so we don't repeat searches) - searched = [] + # track all DNs searched for (so we don't repeat searches) + searched = Set.new # if this is a posixGroup, return members immediately (no nesting) uids = member_uids(group) From 52654ad40bd92932e93089278ae6e79302f14d48 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:56:13 -0800 Subject: [PATCH 159/307] Short circuit after filtering out already searched DNs --- lib/github/ldap/member_search/recursive.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index fa3d5a8..d082de0 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -69,12 +69,12 @@ def perform(group) sub_dns.concat member_dns(subgroup) end - # give up if there's nothing else to search for - break if sub_dns.empty? - # filter out if already searched for sub_dns.reject! { |dn| searched.include?(dn) } + # give up if there's nothing else to search for + break if sub_dns.empty? + # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) From 8fce4a79dc48cffaa67c62d7ab68120427a074ca Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:22:47 -0800 Subject: [PATCH 160/307] Extract group search method --- lib/github/ldap/member_search/recursive.rb | 31 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index d082de0..5ee8420 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -52,9 +52,8 @@ def perform(group) dns = member_dns(group) # search for base group's subgroups - filter = ALL_GROUPS_FILTER groups = dns.each_with_object([]) do |dn, groups| - groups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + groups.concat find_groups_by_dn(dn) searched << dn end @@ -77,13 +76,14 @@ def perform(group) # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| - subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) - searched << dn + subgroups.concat find_groups_by_dn(dn) + searched << dn end + # give up if there were no subgroups found break if subgroups.empty? - # track found groups + # track found subgroups subgroups.each { |g| found[g.dn] = g } # descend another level @@ -106,6 +106,27 @@ def perform(group) entries end + # Internal: Search for Groups by DN. + # + # Given a Distinguished Name (DN) String value, find the Group entry + # that matches it. The DN may map to a `person` entry, but we want to + # filter those out. + # + # This will find zero or one entry most of the time, but it's not + # guaranteed so we account for the possibility of more. + # + # This method is intended to be used with `Array#concat` by the caller. + # + # Returns an Array of zero or more Net::LDAP::Entry objects. + def find_groups_by_dn(dn) + ldap.search \ + base: dn, + scope: Net::LDAP::SearchScope_BaseObject, + attributes: attrs, + filter: ALL_GROUPS_FILTER + end + private :find_group_by_dn + # Internal: Fetch entries by UID. # # Returns an Array of Net::LDAP::Entry objects. From 2e5943b9d822a2a2a277ba2bbca33791ac4a9671 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:23:30 -0800 Subject: [PATCH 161/307] Fix spacing [skip ci] --- lib/github/ldap/member_search/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5ee8420..5235d5a 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -77,7 +77,7 @@ def perform(group) # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| subgroups.concat find_groups_by_dn(dn) - searched << dn + searched << dn end # give up if there were no subgroups found From de8d8b46a8d8dcf25589bd46d1cc41f506e6dd6a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:29:23 -0800 Subject: [PATCH 162/307] Tweak member DN collection, mapping --- lib/github/ldap/member_search/recursive.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5235d5a..bbc557f 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -94,14 +94,14 @@ def perform(group) # entries to return entries = [] - # pull member DNs, discarding dupes and subgroup DNs - member_dns = found.values.each_with_object([]) do |group, member_dns| + # collect all member DNs, discarding dupes and subgroup DNs + members = found.values.each_with_object([]) do |group, dns| entries << group - member_dns.concat member_dns(group) + dns.concat member_dns(group) end.uniq.reject { |dn| found.key?(dn) } # wrap member DNs in Net::LDAP::Entry objects - entries.concat member_dns.map { |dn| Net::LDAP::Entry.new(dn) } + entries.concat members.map! { |dn| Net::LDAP::Entry.new(dn) } entries end From 973959d4f4aec8b62bb5e9f837e0cbe2c206cf68 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:29:35 -0800 Subject: [PATCH 163/307] Fix method name --- lib/github/ldap/member_search/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index bbc557f..a36aa4d 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -125,7 +125,7 @@ def find_groups_by_dn(dn) attributes: attrs, filter: ALL_GROUPS_FILTER end - private :find_group_by_dn + private :find_groups_by_dn # Internal: Fetch entries by UID. # From 196bc17bac97056f99f73853555a922856f741f7 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 19 Jan 2015 17:50:48 -0800 Subject: [PATCH 164/307] Bump version to 1.8.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 3ccbd4e..50ad02d 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.7.1" + spec.version = "1.8.0" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From 5107dc7ee9b761940252935731b00cfd23d21a20 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 19 Jan 2015 17:51:57 -0800 Subject: [PATCH 165/307] Update changelog for 1.8.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b72dbe7..f6d0941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.8.0 + +* Optimize Recursive *Member Search* strategy [#78](https://github.com/github/github-ldap/pull/78) + # v1.7.1 * Add Active Directory group filter [#75](https://github.com/github/github-ldap/pull/75) From 47be59a7e04b13327b19de5ab48520076504e62a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 19 Jan 2015 17:59:03 -0800 Subject: [PATCH 166/307] Release 1.8.0 From e219da1cb86aecd94e09188c930bef655a2b67f7 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 2 Feb 2015 14:30:48 -0800 Subject: [PATCH 167/307] Expand AD caps support to v60 This adds support for Windows Server 2008 and above, instead of only 2008 R2 and above. --- lib/github/ldap.rb | 4 ++-- test/ldap_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index ebdfddc..6384023 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -19,7 +19,7 @@ class Ldap # Internal: The capability required to use ActiveDirectory features. # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze + ACTIVE_DIRECTORY_V60_OID = "1.2.840.113556.1.4.1935".freeze # Utility method to get the last operation result with a human friendly message. # @@ -313,7 +313,7 @@ def configure_member_search_strategy(strategy = nil) # # Returns true if the host is an ActiveDirectory server, false otherwise. def active_directory_capability? - capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID) + capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V60_OID) end private :active_directory_capability? end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 48511d8..119c163 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -80,7 +80,7 @@ def test_search_strategy_defaults def test_search_strategy_detects_active_directory caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V61_R2_OID] + caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V60_OID] @ldap.stub :capabilities, caps do @ldap.configure_search_strategy :detect From 5cfcb2f6457e34ebcd4f175aaf2d5913ee64bc31 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 2 Feb 2015 14:44:59 -0800 Subject: [PATCH 168/307] Bring back further to v51 for 2k3+ support --- lib/github/ldap.rb | 4 ++-- test/ldap_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 6384023..0545247 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -19,7 +19,7 @@ class Ldap # Internal: The capability required to use ActiveDirectory features. # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - ACTIVE_DIRECTORY_V60_OID = "1.2.840.113556.1.4.1935".freeze + ACTIVE_DIRECTORY_V51_OID = "1.2.840.113556.1.4.1670".freeze # Utility method to get the last operation result with a human friendly message. # @@ -313,7 +313,7 @@ def configure_member_search_strategy(strategy = nil) # # Returns true if the host is an ActiveDirectory server, false otherwise. def active_directory_capability? - capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V60_OID) + capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end private :active_directory_capability? end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 119c163..c7b24c4 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -80,7 +80,7 @@ def test_search_strategy_defaults def test_search_strategy_detects_active_directory caps = Net::LDAP::Entry.new - caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V60_OID] + caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V51_OID] @ldap.stub :capabilities, caps do @ldap.configure_search_strategy :detect From ba158a024e5d236a6fa42b51c38fadcfc8595723 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 2 Feb 2015 14:53:39 -0800 Subject: [PATCH 169/307] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6d0941..f09c1c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.8.1 + +* Expand supported ActiveDirectory capabilities to include Windows Server 2003 [#80](https://github.com/github/github-ldap/pull/80) + # v1.8.0 * Optimize Recursive *Member Search* strategy [#78](https://github.com/github/github-ldap/pull/78) From f7c44a1a3cc01e25c5db15f6a8266800df98f603 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 2 Feb 2015 14:54:36 -0800 Subject: [PATCH 170/307] Bump version to 1.8.1 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 50ad02d..fb15c7f 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.8.0" + spec.version = "1.8.1" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From 99ab8c6376c4e53bd602c01f494c0b7b183043d1 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Mon, 2 Feb 2015 14:55:17 -0800 Subject: [PATCH 171/307] Release 1.8.1 From 92f18b196f090d8a518a97ceacaa81705ee38b86 Mon Sep 17 00:00:00 2001 From: Ben Gollmer Date: Fri, 8 May 2015 23:56:07 +0200 Subject: [PATCH 172/307] Compare AD DNs case-insensitively when checking group membership --- .../ldap/membership_validators/active_directory.rb | 3 ++- test/membership_validators/active_directory_test.rb | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 0c531c4..69b9bc8 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -31,7 +31,8 @@ def perform(entry) attributes: ATTRS # membership validated if entry was matched and returned as a result - matched.map(&:dn).include?(entry.dn) + # Active Directory DNs are case-insensitive + matched.map { |m| m.dn.downcase }.include?(entry.dn.downcase) end # Internal: Constructs a membership filter using the "in chain" diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index 0caafe2..956fbc5 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -123,4 +123,13 @@ def test_validates_user_in_posix_group validator = make_validator(%w(posix-group1)) assert validator.perform(@entry) end + + def test_validates_user_in_group_with_differently_cased_dn + validator = make_validator(%w(all-users)) + @entry[:dn].map(&:upcase!) + assert validator.perform(@entry) + + @entry[:dn].map(&:downcase!) + assert validator.perform(@entry) + end end From 094a3d5ed85ecd71cba1351cade527137db6cde2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 14 May 2015 11:36:20 -0700 Subject: [PATCH 173/307] Bump patch version, update changelog --- CHANGELOG.md | 4 ++++ github-ldap.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f09c1c9..edd76c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.8.2 + +* Ignore case when comparing ActiveDirectory DNs [#82](https://github.com/github/github-ldap/pull/82) + # v1.8.1 * Expand supported ActiveDirectory capabilities to include Windows Server 2003 [#80](https://github.com/github/github-ldap/pull/80) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index fb15c7f..c34dc5d 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.8.1" + spec.version = "1.8.2" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From e2f51d09926725d2c0b055fc4489e5c1eeaac51a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 15 May 2015 12:05:11 -0700 Subject: [PATCH 174/307] Depend on net-ldap v0.11.x --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index c34dc5d..fb0bb36 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.10.0' + spec.add_dependency 'net-ldap', '~> 0.11.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From c076b1ada3829e487c6d470384395fa72682269d Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 15 May 2015 12:28:52 -0700 Subject: [PATCH 175/307] Setup GitHub::Ldap instance with correct settings --- test/filter_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/filter_test.rb b/test/filter_test.rb index 58992a8..4da83c9 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -1,6 +1,6 @@ require_relative 'test_helper' -class FilterTest < Minitest::Test +class FilterTest < GitHub::Ldap::Test class Subject include GitHub::Ldap::Filter def initialize(ldap) @@ -16,7 +16,7 @@ def [](field) end def setup - @ldap = GitHub::Ldap.new(:uid => 'uid') + @ldap = GitHub::Ldap.new(options.merge(:uid => 'uid')) @subject = Subject.new(@ldap) @me = 'uid=calavera,dc=github,dc=com' @uid = "calavera" From 42dc1fca788f148c2de7e11ff136ee91a2042ac8 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 15 May 2015 12:39:31 -0700 Subject: [PATCH 176/307] Bump version to 1.9.0, update changelog --- CHANGELOG.md | 4 ++++ github-ldap.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edd76c8..70c0eed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.9.0 + +* Update net-ldap dependency to `~> 0.11.0` [#84](https://github.com/github/github-ldap/pull/84) + # v1.8.2 * Ignore case when comparing ActiveDirectory DNs [#82](https://github.com/github/github-ldap/pull/82) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index fb0bb36..af13ce0 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.8.2" + spec.version = "1.9.0" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From c3b2401634e169434f80514d156aae02d72dc339 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 15 May 2015 12:48:22 -0700 Subject: [PATCH 177/307] Release 1.9.0 From f4da8948695be6637cab4cc10d9192c5fb6347f6 Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Mon, 11 Apr 2016 16:40:18 +0100 Subject: [PATCH 178/307] Bump net-ldap requirement to 0.14.0 We need this so we can take advantage of the `hosts` option --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index af13ce0..eb04367 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.11.0' + spec.add_dependency 'net-ldap', '~> 0.14.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From baca799d8f7d7431116a48c5fc45c949934d4964 Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Mon, 11 Apr 2016 16:41:02 +0100 Subject: [PATCH 179/307] Adds support for the `hosts` option now available in net-ldap --- lib/github/ldap.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 0545247..90c6f92 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -50,6 +50,9 @@ class Ldap # # host: required string ldap server host address # port: required string or number ldap server port + # hosts: an enumerable of pairs of hosts and corresponding ports with + # which to attempt opening connections (default [[host, port]]). Overrides + # host and port if set. # encryption: optional string. `ssl` or `tls`. nil by default # admin_user: optional string ldap administrator user dn for authentication # admin_password: optional string ldap administrator user password @@ -72,6 +75,7 @@ def initialize(options = {}) @connection = Net::LDAP.new({ host: options[:host], port: options[:port], + hosts: options[:hosts], instrumentation_service: options[:instrumentation_service] }) From 65e653987f7138b3ce0518fe1a88deb44f70d2c6 Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Mon, 11 Apr 2016 16:41:39 +0100 Subject: [PATCH 180/307] Add tests for `hosts` option --- test/ldap_test.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index c7b24c4..d00c7a0 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -9,6 +9,21 @@ def test_connection_with_default_options assert @ldap.test_connection, "Ldap connection expected to succeed" end + def test_connection_with_list_of_hosts_with_one_valid_host + ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", 3897]])) + assert ldap.test_connection, "Ldap connection expected to succeed" + end + + def test_connection_with_list_of_hosts_with_first_valid + ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", 3897], ["invalid.local", 3897]])) + assert ldap.test_connection, "Ldap connection expected to succeed" + end + + def test_connection_with_list_of_hosts_with_first_invalid + ldap = GitHub::Ldap.new(options.merge(hosts: [["invalid.local", 3897], ["localhost", 3897]])) + assert ldap.test_connection, "Ldap connection expected to succeed" + end + def test_simple_tls assert_equal :simple_tls, @ldap.check_encryption(:ssl) assert_equal :simple_tls, @ldap.check_encryption('SSL') From a87afd0d562871b741ce6e02968a8b9d9261abcf Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Mon, 11 Apr 2016 16:53:47 +0100 Subject: [PATCH 181/307] Document `hosts` option in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 482f6ea..eb5fb01 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ There are a few configuration options required to use this adapter: * host: is the host address where the ldap server lives. * port: is the port where the ldap server lives. +* hosts: (optional) an enumerable of pairs of hosts and corresponding ports with which to attempt opening connections (default [[host, port]]). Overrides host and port if set. * encryption: is the encryption protocol, disabled by default. The valid options are `ssl` and `tls`. * uid: is the field name in the ldap server used to authenticate your users, in ActiveDirectory this is `sAMAccountName`. From 0e695c622af73498c692901051c03160b388e863 Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Fri, 15 Apr 2016 10:49:55 +0100 Subject: [PATCH 182/307] Bump version --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index eb04367..b098983 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.9.0" + spec.version = "1.9.1" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From c2cc6e58c0ca52411a9a7b58c4e6b172e2f2ff29 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Wed, 13 Jul 2016 09:25:55 -0700 Subject: [PATCH 183/307] bump net-ldap 0.15.0 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index af13ce0..4ad193c 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.11.0' + spec.add_dependency 'net-ldap', '~> 0.15.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From 0c2efc8bf517ba5b3ad199fc18d1beef9fb55ff7 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Wed, 13 Jul 2016 09:31:10 -0700 Subject: [PATCH 184/307] release 1.10.0 --- CHANGELOG.md | 4 ++++ github-ldap.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70c0eed..2ae6339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.10.0 + +* Bump net-ldap to 0.15.0 [#92](https://github.com/github/github-ldap/pull/92) + # v1.9.0 * Update net-ldap dependency to `~> 0.11.0` [#84](https://github.com/github/github-ldap/pull/84) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 4ad193c..b8514d0 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.9.0" + spec.version = "1.10.0" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From 8d66502d246cc5ecbe246b0b06fd69c172020d40 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Wed, 13 Jul 2016 09:31:51 -0700 Subject: [PATCH 185/307] Release 1.10.0 From ecf5c434b0ae2338c45e6c8e28d772b267efb2ae Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 09:29:10 -0500 Subject: [PATCH 186/307] Load a global catalog connection & add interface for searching it --- lib/github/ldap.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 0545247..612e245 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -193,6 +193,20 @@ def search(options, &block) end end + def global_catalog_search(options, &block) + options[:base] = "" + Array(global_catalog_connection.search(options, &block)) + end + + def global_catalog_connection + @global_catalog_connection ||= Net::LDAP.new({ + host: @connection.instance_variable_get(:@host), + auth: @connection.instance_variable_get(:@auth), + instrumentation_service: @connection.instance_variable_get(:@instrumentation_service), + port: 3268, + }) + end + # Internal: Searches the host LDAP server's Root DSE for capabilities and # extensions. # From 51e72c0c206926fdb6e337537cbe07724e9a055b Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 09:30:11 -0500 Subject: [PATCH 187/307] Use global catalog to detect user, if server is Active Directory --- lib/github/ldap.rb | 1 - lib/github/ldap/domain.rb | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 612e245..7f7ac39 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -329,6 +329,5 @@ def configure_member_search_strategy(strategy = nil) def active_directory_capability? capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end - private :active_directory_capability? end end diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index 8fd904f..2332e9c 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -118,7 +118,12 @@ def user?(login, search_options = {}) options = search_options.merge \ filter: login_filter(@uid, login), size: 1 - search(options).first + + if @ldap.active_directory_capability? + global_catalog_search(options).first + else + search(options).first + end end # Check if a user can be bound with a password. @@ -160,6 +165,10 @@ def search(options, &block) @ldap.search(options, &block) end + def global_catalog_search(options, &block) + @ldap.global_catalog_search(options, &block) + end + # Get the entry for this domain. # # Returns a Net::LDAP::Entry From 203063d6e606eed1772cda85c078d2c6ecdc4cd9 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 09:51:40 -0500 Subject: [PATCH 188/307] Add tests for auth & unauth default Global Catalog settings --- Gemfile | 1 + test/ldap_test.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Gemfile b/Gemfile index 4abbfe8..edf9461 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,5 @@ gemspec group :test, :development do gem "byebug", :platforms => [:mri_20, :mri_21] + gem "mocha" end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index c7b24c4..b5e4eb1 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -117,12 +117,40 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end + + module GitHubLdapUnauthenticatedTestCases + def test_global_catalog_default_settings + global_catalog = @ldap.global_catalog_connection + # this is ugly, but currently the only way to test Net::LDAP#auth values + auth = global_catalog.instance_variable_get(:@auth) + + assert_equal "localhost", global_catalog.host + assert_equal 3268, global_catalog.port + assert_equal nil, auth[:password] + assert_equal nil, auth[:username] + end + end + + module GitHubLdapAuthenticatedTestCases + def test_global_catalog_default_settings + global_catalog = @ldap.global_catalog_connection + # this is ugly, but currently the only way to test Net::LDAP#auth values + auth = global_catalog.instance_variable_get(:@auth) + + assert_equal "localhost", global_catalog.host + assert_equal 3268, global_catalog.port + assert_equal "passworD1", auth[:password] + assert_equal "uid=admin,dc=github,dc=com", auth[:username] + end + end end class GitHubLdapTest < GitHub::Ldap::Test include GitHubLdapTestCases + include GitHubLdapAuthenticatedTestCases end class GitHubLdapUnauthenticatedTest < GitHub::Ldap::UnauthenticatedTest include GitHubLdapTestCases + include GitHubLdapUnauthenticatedTestCases end From a23ccef696a825ed32d77396cf1ce165a9f9fecc Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:08:17 -0500 Subject: [PATCH 189/307] Test format of Global Catalog search results --- test/ldap_test.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index b5e4eb1..1401486 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -1,4 +1,5 @@ require_relative 'test_helper' +require 'mocha/mini_test' module GitHubLdapTestCases def setup @@ -118,6 +119,23 @@ def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end + def test_global_catalog_returns_empty_array_for_no_results + mock_global_catalog_connection = Object.new + mock_global_catalog_connection.expects(:search).returns(nil) + Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + results = @ldap.global_catalog_search({}) + assert_equal [], results + end + + def test_global_catalog_returns_array_of_results + mock_global_catalog_connection = Object.new + stub_entry = Object.new + mock_global_catalog_connection.expects(:search).returns(stub_entry) + Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + results = @ldap.global_catalog_search({}) + assert_equal [stub_entry], results + end + module GitHubLdapUnauthenticatedTestCases def test_global_catalog_default_settings global_catalog = @ldap.global_catalog_connection From c03c0de4186eaa05f2bf9c64b1f9f6313be033bb Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:12:07 -0500 Subject: [PATCH 190/307] Split up auth'd, unauth'd and global default settings tests --- test/ldap_test.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 1401486..8efbc00 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -136,27 +136,30 @@ def test_global_catalog_returns_array_of_results assert_equal [stub_entry], results end + def test_global_catalog_default_settings + global_catalog = @ldap.global_catalog_connection + + assert_equal "localhost", global_catalog.host + assert_equal 3268, global_catalog.port + end + module GitHubLdapUnauthenticatedTestCases - def test_global_catalog_default_settings + def test_global_catalog_unauthenticated_default_settings global_catalog = @ldap.global_catalog_connection # this is ugly, but currently the only way to test Net::LDAP#auth values auth = global_catalog.instance_variable_get(:@auth) - assert_equal "localhost", global_catalog.host - assert_equal 3268, global_catalog.port assert_equal nil, auth[:password] assert_equal nil, auth[:username] end end module GitHubLdapAuthenticatedTestCases - def test_global_catalog_default_settings + def test_global_catalog_authenticated_default_settings global_catalog = @ldap.global_catalog_connection # this is ugly, but currently the only way to test Net::LDAP#auth values auth = global_catalog.instance_variable_get(:@auth) - assert_equal "localhost", global_catalog.host - assert_equal 3268, global_catalog.port assert_equal "passworD1", auth[:password] assert_equal "uid=admin,dc=github,dc=com", auth[:username] end From d4ae1ab3e03f5479e59b61645eb497c26061cbb6 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:37:24 -0500 Subject: [PATCH 191/307] Updated documentation --- lib/github/ldap.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 7f7ac39..3c5c8c5 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -198,6 +198,11 @@ def global_catalog_search(options, &block) Array(global_catalog_connection.search(options, &block)) end + # Returns a late-bound, memoized connection to an Active Directory Global Catalog + # if the server is an Active Directory instance, otherwise returns nil. + # + # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx + # def global_catalog_connection @global_catalog_connection ||= Net::LDAP.new({ host: @connection.instance_variable_get(:@host), From 8244f7c484bcd897a215f9cabbe5a97ad35a443c Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:38:26 -0500 Subject: [PATCH 192/307] Only initialize global catalog if server is Active Directory --- lib/github/ldap.rb | 14 ++++++++------ test/ldap_test.rb | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 3c5c8c5..128e3c1 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -204,12 +204,14 @@ def global_catalog_search(options, &block) # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx # def global_catalog_connection - @global_catalog_connection ||= Net::LDAP.new({ - host: @connection.instance_variable_get(:@host), - auth: @connection.instance_variable_get(:@auth), - instrumentation_service: @connection.instance_variable_get(:@instrumentation_service), - port: 3268, - }) + if active_directory_capability? + @global_catalog_connection ||= Net::LDAP.new({ + host: @connection.host, + auth: @connection.instance_variable_get(:@auth), + instrumentation_service: @connection.instance_variable_get(:@instrumentation_service), + port: 3268, + }) + end end # Internal: Searches the host LDAP server's Root DSE for capabilities and diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 8efbc00..30cb1f4 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -119,7 +119,18 @@ def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end + def test_global_catalog_connection_is_null_if_not_active_directory + @ldap.expects(:active_directory_capability?).returns(false) + assert_equal nil, @ldap.global_catalog_connection + end + + def test_global_catalog_connection_is_null_if_not_active_directory + @ldap.expects(:active_directory_capability?).returns(true) + refute_nil @ldap.global_catalog_connection, "Expected Global Catalog to not be nil" + end + def test_global_catalog_returns_empty_array_for_no_results + @ldap.expects(:active_directory_capability?).returns(true) mock_global_catalog_connection = Object.new mock_global_catalog_connection.expects(:search).returns(nil) Net::LDAP.expects(:new).returns(mock_global_catalog_connection) @@ -128,6 +139,7 @@ def test_global_catalog_returns_empty_array_for_no_results end def test_global_catalog_returns_array_of_results + @ldap.expects(:active_directory_capability?).returns(true) mock_global_catalog_connection = Object.new stub_entry = Object.new mock_global_catalog_connection.expects(:search).returns(stub_entry) @@ -137,6 +149,7 @@ def test_global_catalog_returns_array_of_results end def test_global_catalog_default_settings + @ldap.expects(:active_directory_capability?).returns(true) global_catalog = @ldap.global_catalog_connection assert_equal "localhost", global_catalog.host @@ -145,6 +158,7 @@ def test_global_catalog_default_settings module GitHubLdapUnauthenticatedTestCases def test_global_catalog_unauthenticated_default_settings + @ldap.expects(:active_directory_capability?).returns(true) global_catalog = @ldap.global_catalog_connection # this is ugly, but currently the only way to test Net::LDAP#auth values auth = global_catalog.instance_variable_get(:@auth) @@ -156,6 +170,7 @@ def test_global_catalog_unauthenticated_default_settings module GitHubLdapAuthenticatedTestCases def test_global_catalog_authenticated_default_settings + @ldap.expects(:active_directory_capability?).returns(true) global_catalog = @ldap.global_catalog_connection # this is ugly, but currently the only way to test Net::LDAP#auth values auth = global_catalog.instance_variable_get(:@auth) From 3cf1c5adfb98a2f7df2ba0788407b2e964f7903d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:39:10 -0500 Subject: [PATCH 193/307] Make capabilities public so domain can decide whether it's Active Dir --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 128e3c1..deb3809 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -214,7 +214,7 @@ def global_catalog_connection end end - # Internal: Searches the host LDAP server's Root DSE for capabilities and + # Public: Searches the host LDAP server's Root DSE for capabilities and # extensions. # # Returns a Net::LDAP::Entry object. From baef44dc688d3adb43eb0d6227e211d5a417522f Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 10:40:02 -0500 Subject: [PATCH 194/307] Test for default instrumentation service on Global Catalog --- test/ldap_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 30cb1f4..7b24b63 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -151,9 +151,11 @@ def test_global_catalog_returns_array_of_results def test_global_catalog_default_settings @ldap.expects(:active_directory_capability?).returns(true) global_catalog = @ldap.global_catalog_connection + instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) assert_equal "localhost", global_catalog.host assert_equal 3268, global_catalog.port + assert_equal "MockInstrumentationService", instrumentation_service.class.name end module GitHubLdapUnauthenticatedTestCases From e05f40caad036e883778644c350b0d82b6f9647d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 11:01:18 -0500 Subject: [PATCH 195/307] Added doc for global_catalog_search --- lib/github/ldap.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index deb3809..54db4fa 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -193,6 +193,14 @@ def search(options, &block) end end + # Public - Search entries in the configured Active Directory Global Catalog + # + # options: is a hash with the same options that Net::LDAP::Connection#search supports. + # block: is an optional block to pass to the search. + # + # see: http://msdn.microsoft.com/en-us/library/cc223359.aspx. + # + # Returns an Array of Net::LDAP::Entry. def global_catalog_search(options, &block) options[:base] = "" Array(global_catalog_connection.search(options, &block)) From 6125bc5166b9122c85dd666df431ff47c2aa64e6 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 11:01:49 -0500 Subject: [PATCH 196/307] Test for domain to use global catalog if it's Active Directory --- test/domain_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/domain_test.rb b/test/domain_test.rb index 4480e60..25c9349 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -1,4 +1,5 @@ require_relative 'test_helper' +require 'mocha/mini_test' module GitHubLdapDomainTestCases def setup @@ -140,6 +141,12 @@ def test_auth_does_not_bind assert user = @domain.user?('user1') refute @domain.auth(user, 'foo'), 'Expected user not not bind' end + + def test_use_global_catalog_if_active_directory + @ldap.stubs(:active_directory_capability?).returns(true) + @domain.expects(:global_catalog_search).returns([]) + @domain.user?('user1', :attributes => [:cn]) + end end class GitHubLdapDomainTest < GitHub::Ldap::Test From a34264b8a738bb209c4d12537d676ef350b0c394 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 11:23:21 -0500 Subject: [PATCH 197/307] Test for using default search on non-Active Directory servers --- test/domain_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/domain_test.rb b/test/domain_test.rb index 25c9349..207154c 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -147,6 +147,12 @@ def test_use_global_catalog_if_active_directory @domain.expects(:global_catalog_search).returns([]) @domain.user?('user1', :attributes => [:cn]) end + + def test_use_default_search_if_not_active_directory + @ldap.stubs(:active_directory_capability?).returns(false) + @domain.expects(:search).returns([]) + @domain.user?('user1', :attributes => [:cn]) + end end class GitHubLdapDomainTest < GitHub::Ldap::Test From a00750f4bde4cb73da754e70fc939f5925a04eb5 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 11:35:37 -0500 Subject: [PATCH 198/307] Keep reference to credentials & instrumentation service for Global Catalog --- lib/github/ldap.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 54db4fa..aae6295 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -68,6 +68,9 @@ class Ldap # def initialize(options = {}) @uid = options[:uid] || "sAMAccountName" + @instrumentation_service = options[:instrumentation_service] + @admin_user = options[:admin_user] + @admin_password = options[:admin_password] @connection = Net::LDAP.new({ host: options[:host], @@ -215,8 +218,8 @@ def global_catalog_connection if active_directory_capability? @global_catalog_connection ||= Net::LDAP.new({ host: @connection.host, - auth: @connection.instance_variable_get(:@auth), - instrumentation_service: @connection.instance_variable_get(:@instrumentation_service), + auth: {username: admin_user, password: admin_password}, + instrumentation_service: instrumentation_service, port: 3268, }) end @@ -344,5 +347,9 @@ def configure_member_search_strategy(strategy = nil) def active_directory_capability? capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end + + private + + attr_reader :admin_user, :admin_password end end From 678758594b40b808b20d3407a96ac473204ef06f Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 12:26:33 -0500 Subject: [PATCH 199/307] Updates to Domain#user? & use of Global Catalog - Set empty search base in user? rather than in GitHub::Ldap - Get rid of indirection in Domain to global_catalog_search - Update test --- lib/github/ldap.rb | 1 - lib/github/ldap/domain.rb | 9 ++++----- test/domain_test.rb | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index aae6295..41153fe 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -205,7 +205,6 @@ def search(options, &block) # # Returns an Array of Net::LDAP::Entry. def global_catalog_search(options, &block) - options[:base] = "" Array(global_catalog_connection.search(options, &block)) end diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index 2332e9c..d6b6caa 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -120,7 +120,10 @@ def user?(login, search_options = {}) size: 1 if @ldap.active_directory_capability? - global_catalog_search(options).first + # when doing a global search for a user's DN, set the search base to blank + options[:base] = "" + @ldap.global_catalog_search(options) + else search(options).first end @@ -165,10 +168,6 @@ def search(options, &block) @ldap.search(options, &block) end - def global_catalog_search(options, &block) - @ldap.global_catalog_search(options, &block) - end - # Get the entry for this domain. # # Returns a Net::LDAP::Entry diff --git a/test/domain_test.rb b/test/domain_test.rb index 207154c..b0d9682 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -144,7 +144,7 @@ def test_auth_does_not_bind def test_use_global_catalog_if_active_directory @ldap.stubs(:active_directory_capability?).returns(true) - @domain.expects(:global_catalog_search).returns([]) + @ldap.expects(:global_catalog_search).returns([]) @domain.user?('user1', :attributes => [:cn]) end From c38ffca4a4ac860133e94e582efc9ea8d2e875ce Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 13:27:42 -0500 Subject: [PATCH 200/307] Test that global catalog search uses empty base DN --- test/domain_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/domain_test.rb b/test/domain_test.rb index b0d9682..ee67f8c 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -148,6 +148,12 @@ def test_use_global_catalog_if_active_directory @domain.user?('user1', :attributes => [:cn]) end + def test_global_catalog_has_empty_search_base + @ldap.stubs(:active_directory_capability?).returns(true) + @ldap.expects(:global_catalog_search).with(has_entry(:base => "")).returns([]) + @domain.user?('user1', :attributes => [:cn]) + end + def test_use_default_search_if_not_active_directory @ldap.stubs(:active_directory_capability?).returns(false) @domain.expects(:search).returns([]) From ab2d49a1b49af0dcfdd5e7901c671929e62273f3 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 13:46:01 -0500 Subject: [PATCH 201/307] Set auth method for Global Catalog explicitly to :simple --- lib/github/ldap.rb | 2 +- test/ldap_test.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 41153fe..01397ab 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -217,7 +217,7 @@ def global_catalog_connection if active_directory_capability? @global_catalog_connection ||= Net::LDAP.new({ host: @connection.host, - auth: {username: admin_user, password: admin_password}, + auth: {method: :simple, username: admin_user, password: admin_password}, instrumentation_service: instrumentation_service, port: 3268, }) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 7b24b63..3c60534 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -153,6 +153,8 @@ def test_global_catalog_default_settings global_catalog = @ldap.global_catalog_connection instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) + auth = global_catalog.instance_variable_get(:@auth) + assert_equal :simple, auth[:method] assert_equal "localhost", global_catalog.host assert_equal 3268, global_catalog.port assert_equal "MockInstrumentationService", instrumentation_service.class.name From 3af0a8856112f7f6e17ebc707b79896e973c6103 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 13:49:15 -0500 Subject: [PATCH 202/307] Make sure global catalog search returns first entry from result array --- lib/github/ldap/domain.rb | 3 +-- test/domain_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index d6b6caa..7df8363 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -122,8 +122,7 @@ def user?(login, search_options = {}) if @ldap.active_directory_capability? # when doing a global search for a user's DN, set the search base to blank options[:base] = "" - @ldap.global_catalog_search(options) - + @ldap.global_catalog_search(options).first else search(options).first end diff --git a/test/domain_test.rb b/test/domain_test.rb index ee67f8c..4d541b3 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -148,6 +148,14 @@ def test_use_global_catalog_if_active_directory @domain.user?('user1', :attributes => [:cn]) end + def test_global_catalog_search_returns_first_entry + @ldap.stubs(:active_directory_capability?).returns(true) + entry = Object.new + @ldap.expects(:global_catalog_search).returns([entry]) + user = @domain.user?('user1', :attributes => [:cn]) + assert_equal entry, user + end + def test_global_catalog_has_empty_search_base @ldap.stubs(:active_directory_capability?).returns(true) @ldap.expects(:global_catalog_search).with(has_entry(:base => "")).returns([]) From dd24ee7076d5e7039daa06be0bf565e984f33e61 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 17:28:05 -0500 Subject: [PATCH 203/307] Document reason for auth references; remove redundant ivar --- lib/github/ldap.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 01397ab..5ac0444 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -68,7 +68,8 @@ class Ldap # def initialize(options = {}) @uid = options[:uid] || "sAMAccountName" - @instrumentation_service = options[:instrumentation_service] + + # Keep a reference to these as default auth for a Global Catalog if needed @admin_user = options[:admin_user] @admin_password = options[:admin_password] From 8d9eca37f9b249d5f68ea3a6effb6e3af60f9869 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 17:35:53 -0500 Subject: [PATCH 204/307] Update doc --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 5ac0444..b93c437 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -209,7 +209,7 @@ def global_catalog_search(options, &block) Array(global_catalog_connection.search(options, &block)) end - # Returns a late-bound, memoized connection to an Active Directory Global Catalog + # Returns a memoized connection to an Active Directory Global Catalog # if the server is an Active Directory instance, otherwise returns nil. # # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx From 76db68a6d39cb385ee2edf32750c6e3b70560533 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 17:37:35 -0500 Subject: [PATCH 205/307] Added test group to Gemfile --- Gemfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index edf9461..a409814 100644 --- a/Gemfile +++ b/Gemfile @@ -5,5 +5,8 @@ gemspec group :test, :development do gem "byebug", :platforms => [:mri_20, :mri_21] +end + +group :test do gem "mocha" end From d69b0ba564322b249d3cad5bb1a85f523290a30d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 17:39:19 -0500 Subject: [PATCH 206/307] Use assert_nil --- test/ldap_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 3c60534..39158a5 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -121,7 +121,7 @@ def test_capabilities def test_global_catalog_connection_is_null_if_not_active_directory @ldap.expects(:active_directory_capability?).returns(false) - assert_equal nil, @ldap.global_catalog_connection + assert_nil @ldap.global_catalog_connection end def test_global_catalog_connection_is_null_if_not_active_directory From 9e9f9e961fd9976a7534117ee4dc3af746dfba65 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 25 Jul 2016 17:40:16 -0500 Subject: [PATCH 207/307] Drop message in test --- test/ldap_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 39158a5..42de9ad 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -126,7 +126,7 @@ def test_global_catalog_connection_is_null_if_not_active_directory def test_global_catalog_connection_is_null_if_not_active_directory @ldap.expects(:active_directory_capability?).returns(true) - refute_nil @ldap.global_catalog_connection, "Expected Global Catalog to not be nil" + refute_nil @ldap.global_catalog_connection end def test_global_catalog_returns_empty_array_for_no_results From ad67b78cb44825ac90ea29ad993b2db92fe9ed8a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 17:44:48 -0500 Subject: [PATCH 208/307] Created new ActiveDirectory user search class; moved tests --- lib/github/ldap.rb | 4 ++ .../ldap/user_search/active_directory.rb | 44 ++++++++++++ test/ldap_test.rb | 65 ----------------- test/user_search/active_directory_test.rb | 71 +++++++++++++++++++ 4 files changed, 119 insertions(+), 65 deletions(-) create mode 100644 lib/github/ldap/user_search/active_directory.rb create mode 100644 test/user_search/active_directory_test.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index b93c437..5071a4f 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -10,6 +10,10 @@ require 'github/ldap/instrumentation' require 'github/ldap/member_search' require 'github/ldap/membership_validators' +require 'github/ldap/connection_cache' +require 'github/ldap/referral_chaser' +require 'github/ldap/url' +require 'github/ldap/user_search/active_directory.rb' module GitHub class Ldap diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb new file mode 100644 index 0000000..a32b0c3 --- /dev/null +++ b/lib/github/ldap/user_search/active_directory.rb @@ -0,0 +1,44 @@ +module GitHub + class Ldap + module UserSearch + class ActiveDirectory < Default + + def initialize(ldap) + @ldap = ldap + end + + def search(options) + # when doing a global search for a user's DN, set the search base to blank + options[:base] = "" + global_catalog_search(options).first + end + + def global_catalog_search(options, &block) + Array(global_catalog_connection.search(options, &block)) + end + + # Returns a memoized connection to an Active Directory Global Catalog + # if the server is an Active Directory instance, otherwise returns nil. + # + # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx + # + def global_catalog_connection + @global_catalog_connection ||= Net::LDAP.new({ + host: ldap.instance_variable_get(:@host), + auth: { + method: :simple, + username: ldap.instance_variable_get(:@admin_user), + password: ldap.instance_variable_get(:@admin_password) + }, + instrumentation_service: ldap.instrumentation_service, + port: 3268, + }) + end + + private + + attr_reader :ldap + end + end + end +end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 42de9ad..5ab9fc3 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -118,71 +118,6 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end - - def test_global_catalog_connection_is_null_if_not_active_directory - @ldap.expects(:active_directory_capability?).returns(false) - assert_nil @ldap.global_catalog_connection - end - - def test_global_catalog_connection_is_null_if_not_active_directory - @ldap.expects(:active_directory_capability?).returns(true) - refute_nil @ldap.global_catalog_connection - end - - def test_global_catalog_returns_empty_array_for_no_results - @ldap.expects(:active_directory_capability?).returns(true) - mock_global_catalog_connection = Object.new - mock_global_catalog_connection.expects(:search).returns(nil) - Net::LDAP.expects(:new).returns(mock_global_catalog_connection) - results = @ldap.global_catalog_search({}) - assert_equal [], results - end - - def test_global_catalog_returns_array_of_results - @ldap.expects(:active_directory_capability?).returns(true) - mock_global_catalog_connection = Object.new - stub_entry = Object.new - mock_global_catalog_connection.expects(:search).returns(stub_entry) - Net::LDAP.expects(:new).returns(mock_global_catalog_connection) - results = @ldap.global_catalog_search({}) - assert_equal [stub_entry], results - end - - def test_global_catalog_default_settings - @ldap.expects(:active_directory_capability?).returns(true) - global_catalog = @ldap.global_catalog_connection - instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) - - auth = global_catalog.instance_variable_get(:@auth) - assert_equal :simple, auth[:method] - assert_equal "localhost", global_catalog.host - assert_equal 3268, global_catalog.port - assert_equal "MockInstrumentationService", instrumentation_service.class.name - end - - module GitHubLdapUnauthenticatedTestCases - def test_global_catalog_unauthenticated_default_settings - @ldap.expects(:active_directory_capability?).returns(true) - global_catalog = @ldap.global_catalog_connection - # this is ugly, but currently the only way to test Net::LDAP#auth values - auth = global_catalog.instance_variable_get(:@auth) - - assert_equal nil, auth[:password] - assert_equal nil, auth[:username] - end - end - - module GitHubLdapAuthenticatedTestCases - def test_global_catalog_authenticated_default_settings - @ldap.expects(:active_directory_capability?).returns(true) - global_catalog = @ldap.global_catalog_connection - # this is ugly, but currently the only way to test Net::LDAP#auth values - auth = global_catalog.instance_variable_get(:@auth) - - assert_equal "passworD1", auth[:password] - assert_equal "uid=admin,dc=github,dc=com", auth[:username] - end - end end class GitHubLdapTest < GitHub::Ldap::Test diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb new file mode 100644 index 0000000..a38943b --- /dev/null +++ b/test/user_search/active_directory_test.rb @@ -0,0 +1,71 @@ +require_relative '../test_helper' +require 'mocha/mini_test' + +class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test + + def setup + @ldap = GitHub::Ldap.new(options) + @ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(@ldap) + end + + def test_global_catalog_connection_is_null_if_not_active_directory + @ad_user_search.expects(:active_directory_capability?).returns(false) + assert_nil @ad_user_search.global_catalog_connection + end + + def test_global_catalog_connection_is_null_if_not_active_directory + refute_nil @ad_user_search.global_catalog_connection + end + + def test_global_catalog_returns_empty_array_for_no_results + mock_global_catalog_connection = Object.new + mock_global_catalog_connection.expects(:search).returns(nil) + Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + results = @ad_user_search.global_catalog_search({}) + assert_equal [], results + end + + def test_global_catalog_returns_array_of_results + mock_global_catalog_connection = Object.new + stub_entry = Object.new + mock_global_catalog_connection.expects(:search).returns(stub_entry) + Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + results = @ad_user_search.global_catalog_search({}) + assert_equal [stub_entry], results + end + + def test_global_catalog_default_settings + global_catalog = @ad_user_search.global_catalog_connection + instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) + + auth = global_catalog.instance_variable_get(:@auth) + assert_equal :simple, auth[:method] + assert_equal "127.0.0.1", global_catalog.host + assert_equal 3268, global_catalog.port + assert_equal "MockInstrumentationService", instrumentation_service.class.name + end + + module GitHubLdapUnauthenticatedTestCases + def test_global_catalog_unauthenticated_default_settings + @ad_user_search.expects(:active_directory_capability?).returns(true) + global_catalog = @ad_user_search.global_catalog_connection + # this is ugly, but currently the only way to test Net::LDAP#auth values + auth = global_catalog.instance_variable_get(:@auth) + + assert_equal nil, auth[:password] + assert_equal nil, auth[:username] + end + end + + module GitHubLdapAuthenticatedTestCases + def test_global_catalog_authenticated_default_settings + @ad_user_search.expects(:active_directory_capability?).returns(true) + global_catalog = @ad_user_search.global_catalog_connection + # this is ugly, but currently the only way to test Net::LDAP#auth values + auth = global_catalog.instance_variable_get(:@auth) + + assert_equal "passworD1", auth[:password] + assert_equal "uid=admin,dc=github,dc=com", auth[:username] + end + end +end From 73ddf22c0528be4bbbff7f205788fb27c4aad8d1 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:08:07 -0500 Subject: [PATCH 209/307] Added default strategy class for UserSearch --- lib/github/ldap/user_search/default.rb | 31 ++++++++++++++++++++++++++ test/user_search/default_test.rb | 20 +++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 lib/github/ldap/user_search/default.rb create mode 100644 test/user_search/default_test.rb diff --git a/lib/github/ldap/user_search/default.rb b/lib/github/ldap/user_search/default.rb new file mode 100644 index 0000000..b9df550 --- /dev/null +++ b/lib/github/ldap/user_search/default.rb @@ -0,0 +1,31 @@ +module GitHub + class Ldap + module UserSearch + class Default + include Filter + + def initialize(ldap) + @ldap = ldap + @options = {} + @options[:attributes] = [] + @options[:paged_searches_supported] = true + @options[:size] = 1 + end + + def perform(login, base_name, uid, search_options) + search_options[:filter] = login_filter(uid, login) + search_options[:base] = base_name + search(search_options.merge(options)) + end + + def search(options) + ldap.search(options) + end + + private + + attr_reader :options, :ldap + end + end + end +end diff --git a/test/user_search/default_test.rb b/test/user_search/default_test.rb new file mode 100644 index 0000000..939e7ff --- /dev/null +++ b/test/user_search/default_test.rb @@ -0,0 +1,20 @@ +require_relative '../test_helper' +require 'mocha/mini_test' + +class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test + def setup + @ldap = GitHub::Ldap.new(options) + @default_user_search = GitHub::Ldap::UserSearch::Default.new(@ldap) + end + + def test_default_search_options + @ldap.expects(:search).with(has_entries( + attributes: [], + size: 1, + paged_searches_supported: true, + base: "CN=HI,CN=McDunnough", + filter: kind_of(Net::LDAP::Filter) + )) + results = @default_user_search.perform("","CN=HI,CN=McDunnough","",{}) + end +end From 3d73e879bf3094f0f4170d704ba57290aabbc115 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:09:36 -0500 Subject: [PATCH 210/307] Load the default user search class; use strategy for Domain#user? --- lib/github/ldap.rb | 4 +--- lib/github/ldap/domain.rb | 12 +----------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 5071a4f..01aa5a6 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -10,9 +10,7 @@ require 'github/ldap/instrumentation' require 'github/ldap/member_search' require 'github/ldap/membership_validators' -require 'github/ldap/connection_cache' -require 'github/ldap/referral_chaser' -require 'github/ldap/url' +require 'github/ldap/user_search/default.rb' require 'github/ldap/user_search/active_directory.rb' module GitHub diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index 7df8363..76aa2dc 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -115,17 +115,7 @@ def valid_login?(login, password) # Returns the user if the login matches any `uid`. # Returns nil if there are no matches. def user?(login, search_options = {}) - options = search_options.merge \ - filter: login_filter(@uid, login), - size: 1 - - if @ldap.active_directory_capability? - # when doing a global search for a user's DN, set the search base to blank - options[:base] = "" - @ldap.global_catalog_search(options).first - else - search(options).first - end + @ldap.ldap_user_search_strategy.perform(login, @base_name, @uid, search_options) end # Check if a user can be bound with a password. From 3f1838c75ee72ed49d6d5eb69aa6befee5e95a4b Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:11:43 -0500 Subject: [PATCH 211/307] Override search for AD user search --- lib/github/ldap/user_search/active_directory.rb | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index a32b0c3..a31cc9a 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -3,18 +3,12 @@ class Ldap module UserSearch class ActiveDirectory < Default - def initialize(ldap) - @ldap = ldap - end - - def search(options) + # Public - Overridden from base class to set the base to "", and use the + # Global Catalog to perform the user search. + def search(search_options) # when doing a global search for a user's DN, set the search base to blank options[:base] = "" - global_catalog_search(options).first - end - - def global_catalog_search(options, &block) - Array(global_catalog_connection.search(options, &block)) + Array(global_catalog_connection.search(search_options.merge(options))) end # Returns a memoized connection to an Active Directory Global Catalog From 115abb7bfffa7bf811ad9d4fab79d74d74fbdfe2 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:12:10 -0500 Subject: [PATCH 212/307] Update tests with new method signature for UserSearch#perform --- test/user_search/active_directory_test.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index a38943b..e11eef7 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -8,20 +8,11 @@ def setup @ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(@ldap) end - def test_global_catalog_connection_is_null_if_not_active_directory - @ad_user_search.expects(:active_directory_capability?).returns(false) - assert_nil @ad_user_search.global_catalog_connection - end - - def test_global_catalog_connection_is_null_if_not_active_directory - refute_nil @ad_user_search.global_catalog_connection - end - def test_global_catalog_returns_empty_array_for_no_results mock_global_catalog_connection = Object.new mock_global_catalog_connection.expects(:search).returns(nil) Net::LDAP.expects(:new).returns(mock_global_catalog_connection) - results = @ad_user_search.global_catalog_search({}) + results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [], results end @@ -30,10 +21,17 @@ def test_global_catalog_returns_array_of_results stub_entry = Object.new mock_global_catalog_connection.expects(:search).returns(stub_entry) Net::LDAP.expects(:new).returns(mock_global_catalog_connection) - results = @ad_user_search.global_catalog_search({}) + results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [stub_entry], results end + def test_searches_with_empty_base_dn + mock_global_catalog_connection = Object.new + mock_global_catalog_connection.expects(:search).with(has_entry(:base => "")) + Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + @ad_user_search.perform("login", "CN=Joe", "uid", {}) + end + def test_global_catalog_default_settings global_catalog = @ad_user_search.global_catalog_connection instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) From f550ce5dd7010cb11ba829bf15cd72532e5b314d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:53:57 -0500 Subject: [PATCH 213/307] Configure user search strategy --- lib/github/ldap.rb | 16 +++++++++++++++- lib/github/ldap/domain.rb | 2 +- test/ldap_test.rb | 6 ++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 01aa5a6..4c8c2f5 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -44,7 +44,8 @@ class Ldap attr_reader :uid, :search_domains, :virtual_attributes, :membership_validator, :member_search_strategy, - :instrumentation_service + :instrumentation_service, + :user_search_strategy # Build a new GitHub::Ldap instance # @@ -104,6 +105,9 @@ def initialize(options = {}) # configure both the membership validator and the member search strategies configure_search_strategy(options[:search_strategy]) + # configure both the membership validator and the member search strategies + configure_user_search_strategy(options[:global_catalog_user_search]) + # enables instrumenting queries @instrumentation_service = options[:instrumentation_service] end @@ -315,6 +319,16 @@ def configure_membership_validation_strategy(strategy = nil) end end + def configure_user_search_strategy(global_catalog_user_search) + @user_search_strategy = begin + if global_catalog_user_search && active_directory_capability? + GitHub::Ldap::UserSearch::ActiveDirectory.new(self) + else + GitHub::Ldap::UserSearch::Default.new(self) + end + end + end + # Internal: Configure the member search strategy. # # diff --git a/lib/github/ldap/domain.rb b/lib/github/ldap/domain.rb index 76aa2dc..07af950 100644 --- a/lib/github/ldap/domain.rb +++ b/lib/github/ldap/domain.rb @@ -115,7 +115,7 @@ def valid_login?(login, password) # Returns the user if the login matches any `uid`. # Returns nil if there are no matches. def user?(login, search_options = {}) - @ldap.ldap_user_search_strategy.perform(login, @base_name, @uid, search_options) + @ldap.user_search_strategy.perform(login, @base_name, @uid, search_options).first end # Check if a user can be bound with a password. diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 5ab9fc3..38295e7 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -115,6 +115,12 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end + def test_user_search_strategy_active_directory_when_configured_and_has_capability + @ldap.configure_user_search_strategy true + @ldap.stubs(:active_directory_capability).returns(false) + assert_equal GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy.class + end + def test_capabilities assert_kind_of Net::LDAP::Entry, @ldap.capabilities end From 32840bc2f90aa0c1714393a3095a45e36dd4db68 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 22:54:32 -0500 Subject: [PATCH 214/307] Removed dead code -- was moved to its own class --- lib/github/ldap.rb | 28 ---------------------------- test/ldap_test.rb | 2 -- 2 files changed, 30 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 4c8c2f5..5348a2e 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -203,34 +203,6 @@ def search(options, &block) end end - # Public - Search entries in the configured Active Directory Global Catalog - # - # options: is a hash with the same options that Net::LDAP::Connection#search supports. - # block: is an optional block to pass to the search. - # - # see: http://msdn.microsoft.com/en-us/library/cc223359.aspx. - # - # Returns an Array of Net::LDAP::Entry. - def global_catalog_search(options, &block) - Array(global_catalog_connection.search(options, &block)) - end - - # Returns a memoized connection to an Active Directory Global Catalog - # if the server is an Active Directory instance, otherwise returns nil. - # - # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx - # - def global_catalog_connection - if active_directory_capability? - @global_catalog_connection ||= Net::LDAP.new({ - host: @connection.host, - auth: {method: :simple, username: admin_user, password: admin_password}, - instrumentation_service: instrumentation_service, - port: 3268, - }) - end - end - # Public: Searches the host LDAP server's Root DSE for capabilities and # extensions. # diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 38295e7..d11e13f 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -128,10 +128,8 @@ def test_capabilities class GitHubLdapTest < GitHub::Ldap::Test include GitHubLdapTestCases - include GitHubLdapAuthenticatedTestCases end class GitHubLdapUnauthenticatedTest < GitHub::Ldap::UnauthenticatedTest include GitHubLdapTestCases - include GitHubLdapUnauthenticatedTestCases end From 89eca1e3e9b6ac7bfe1016db766538f93c1c376a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 23:18:34 -0500 Subject: [PATCH 215/307] More tests for user search strategy --- test/ldap_test.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index d11e13f..9826b5b 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -116,8 +116,19 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de end def test_user_search_strategy_active_directory_when_configured_and_has_capability + @ldap.stubs(:active_directory_capability?).returns(true) @ldap.configure_user_search_strategy true - @ldap.stubs(:active_directory_capability).returns(false) + assert_equal GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy.class + end + + def test_user_search_strategy_not_active_directory_without_capability + @ldap.stubs(:active_directory_capability?).returns(false) + @ldap.configure_user_search_strategy true + assert_equal GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy.class + end + + def test_user_search_strategy_default_when_configured + @ldap.configure_user_search_strategy false assert_equal GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy.class end From faf1a16b6d0103c7edac1afa2cfc40fb8fdf0092 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 23:28:32 -0500 Subject: [PATCH 216/307] Reverse the merge order for default search --- lib/github/ldap/user_search/default.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/user_search/default.rb b/lib/github/ldap/user_search/default.rb index b9df550..88f723b 100644 --- a/lib/github/ldap/user_search/default.rb +++ b/lib/github/ldap/user_search/default.rb @@ -15,7 +15,7 @@ def initialize(ldap) def perform(login, base_name, uid, search_options) search_options[:filter] = login_filter(uid, login) search_options[:base] = base_name - search(search_options.merge(options)) + search(options.merge(search_options)) end def search(options) From 2538aa0be449fd61ec61989cb5a66ed38a44a6ff Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 23:38:07 -0500 Subject: [PATCH 217/307] Updated domain tests since adding user search strategy --- test/domain_test.rb | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 4d541b3..92b9031 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -132,6 +132,14 @@ def test_user_returns_subset_of_attributes assert_equal [:dn, :cn], entry.attribute_names end + def test_user_returns_subset_of_attributes_for_global_catalog_search + @ldap.stubs(:active_directory_capability?).returns(true) + @ldap.configure_user_search_strategy(true) + assert_equal GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy.class + assert entry = @domain.user?('user1', :attributes => [:cn]) + assert_equal [:dn, :cn], entry.attribute_names + end + def test_auth_binds assert user = @domain.user?('user1') assert @domain.auth(user, 'passworD1'), 'Expected user to bind' @@ -142,31 +150,14 @@ def test_auth_does_not_bind refute @domain.auth(user, 'foo'), 'Expected user not not bind' end - def test_use_global_catalog_if_active_directory - @ldap.stubs(:active_directory_capability?).returns(true) - @ldap.expects(:global_catalog_search).returns([]) - @domain.user?('user1', :attributes => [:cn]) - end - - def test_global_catalog_search_returns_first_entry - @ldap.stubs(:active_directory_capability?).returns(true) + def test_user_search_returns_first_entry entry = Object.new - @ldap.expects(:global_catalog_search).returns([entry]) + search_strategy = Object.new + search_strategy.stubs(:perform).returns([entry]) + @ldap.expects(:user_search_strategy).returns(search_strategy) user = @domain.user?('user1', :attributes => [:cn]) assert_equal entry, user end - - def test_global_catalog_has_empty_search_base - @ldap.stubs(:active_directory_capability?).returns(true) - @ldap.expects(:global_catalog_search).with(has_entry(:base => "")).returns([]) - @domain.user?('user1', :attributes => [:cn]) - end - - def test_use_default_search_if_not_active_directory - @ldap.stubs(:active_directory_capability?).returns(false) - @domain.expects(:search).returns([]) - @domain.user?('user1', :attributes => [:cn]) - end end class GitHubLdapDomainTest < GitHub::Ldap::Test From d7590754c656da59b02e3c2ef29bc93ef7414aa5 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 23:39:26 -0500 Subject: [PATCH 218/307] Removed unnecessary tests --- test/user_search/active_directory_test.rb | 24 ----------------------- 1 file changed, 24 deletions(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index e11eef7..7015686 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -42,28 +42,4 @@ def test_global_catalog_default_settings assert_equal 3268, global_catalog.port assert_equal "MockInstrumentationService", instrumentation_service.class.name end - - module GitHubLdapUnauthenticatedTestCases - def test_global_catalog_unauthenticated_default_settings - @ad_user_search.expects(:active_directory_capability?).returns(true) - global_catalog = @ad_user_search.global_catalog_connection - # this is ugly, but currently the only way to test Net::LDAP#auth values - auth = global_catalog.instance_variable_get(:@auth) - - assert_equal nil, auth[:password] - assert_equal nil, auth[:username] - end - end - - module GitHubLdapAuthenticatedTestCases - def test_global_catalog_authenticated_default_settings - @ad_user_search.expects(:active_directory_capability?).returns(true) - global_catalog = @ad_user_search.global_catalog_connection - # this is ugly, but currently the only way to test Net::LDAP#auth values - auth = global_catalog.instance_variable_get(:@auth) - - assert_equal "passworD1", auth[:password] - assert_equal "uid=admin,dc=github,dc=com", auth[:username] - end - end end From 1ab77c0e0b84b79668d47ad1b03b4613914c9997 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 3 Aug 2016 23:40:41 -0500 Subject: [PATCH 219/307] Don't need this test --- test/domain_test.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 92b9031..324a41a 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -132,14 +132,6 @@ def test_user_returns_subset_of_attributes assert_equal [:dn, :cn], entry.attribute_names end - def test_user_returns_subset_of_attributes_for_global_catalog_search - @ldap.stubs(:active_directory_capability?).returns(true) - @ldap.configure_user_search_strategy(true) - assert_equal GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy.class - assert entry = @domain.user?('user1', :attributes => [:cn]) - assert_equal [:dn, :cn], entry.attribute_names - end - def test_auth_binds assert user = @domain.user?('user1') assert @domain.auth(user, 'passworD1'), 'Expected user to bind' From 2e33e8bb8c41821a0af5aa3aa63f2c15d251a104 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:11:55 -0500 Subject: [PATCH 220/307] Clean up requires --- lib/github/ldap.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 5348a2e..66c84f3 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -10,8 +10,8 @@ require 'github/ldap/instrumentation' require 'github/ldap/member_search' require 'github/ldap/membership_validators' -require 'github/ldap/user_search/default.rb' -require 'github/ldap/user_search/active_directory.rb' +require 'github/ldap/user_search/default' +require 'github/ldap/user_search/active_directory' module GitHub class Ldap From cf59b4cea4dc4228a4b534462404cdd204220224 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:12:36 -0500 Subject: [PATCH 221/307] Created a GlobalCatalog object; expose :connection on LDAP --- lib/github/ldap.rb | 3 +- .../ldap/user_search/active_directory.rb | 33 ++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 66c84f3..71ec6fe 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -45,7 +45,8 @@ class Ldap :membership_validator, :member_search_strategy, :instrumentation_service, - :user_search_strategy + :user_search_strategy, + :connection # Build a new GitHub::Ldap instance # diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index a31cc9a..27e2231 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -17,21 +17,30 @@ def search(search_options) # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx # def global_catalog_connection - @global_catalog_connection ||= Net::LDAP.new({ - host: ldap.instance_variable_get(:@host), - auth: { - method: :simple, - username: ldap.instance_variable_get(:@admin_user), - password: ldap.instance_variable_get(:@admin_password) - }, - instrumentation_service: ldap.instrumentation_service, - port: 3268, - }) + GlobalCatalog.connection(ldap) end + end + + class GlobalCatalog < Net::LDAP + STANDARD_GC_PORT = 3268 + LDAPS_GC_PORT = 3269 - private + def self.connection(ldap) + @global_catalog_instance ||= begin + netldap = ldap.connection + # This is ugly, but Net::LDAP doesn't expose encryption or auth + encryption = netldap.instance_variable_get(:@encryption) + auth = netldap.instance_variable_get(:@auth) - attr_reader :ldap + new({ + host: ldap.instance_variable_get(:@host), + instrumentation_service: ldap.instrumentation_service, + port: encryption ? LDAPS_GC_PORT : STANDARD_GC_PORT, + auth: auth, + encryption: encryption + }) + end + end end end end From 0b22addb4b34cf6835846866b6dba9649551a4d2 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:13:24 -0500 Subject: [PATCH 222/307] Configure user search consistent with other config strategies --- lib/github/ldap.rb | 15 +++++++++++---- test/ldap_test.rb | 14 +++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 71ec6fe..7ec20cd 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -107,7 +107,7 @@ def initialize(options = {}) configure_search_strategy(options[:search_strategy]) # configure both the membership validator and the member search strategies - configure_user_search_strategy(options[:global_catalog_user_search]) + configure_user_search_strategy(options[:user_search_strategy]) # enables instrumenting queries @instrumentation_service = options[:instrumentation_service] @@ -292,10 +292,17 @@ def configure_membership_validation_strategy(strategy = nil) end end - def configure_user_search_strategy(global_catalog_user_search) + def configure_user_search_strategy(strategy) @user_search_strategy = begin - if global_catalog_user_search && active_directory_capability? - GitHub::Ldap::UserSearch::ActiveDirectory.new(self) + case strategy.to_s + when "default" + GitHub::Ldap::UserSearch::Default.new(self) + when "global_catalog" + if active_directory_capability? + GitHub::Ldap::UserSearch::ActiveDirectory.new(self) + else + GitHub::Ldap::UserSearch::Default.new(self) + end else GitHub::Ldap::UserSearch::Default.new(self) end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 9826b5b..0af1cb4 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -115,21 +115,21 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end - def test_user_search_strategy_active_directory_when_configured_and_has_capability + def test_user_search_strategy_global_catalog_when_configured_and_has_capability @ldap.stubs(:active_directory_capability?).returns(true) - @ldap.configure_user_search_strategy true - assert_equal GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy.class + @ldap.configure_user_search_strategy("global_catalog") + assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy end def test_user_search_strategy_not_active_directory_without_capability @ldap.stubs(:active_directory_capability?).returns(false) - @ldap.configure_user_search_strategy true - assert_equal GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy.class + @ldap.configure_user_search_strategy("global_catalog") + assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy end def test_user_search_strategy_default_when_configured - @ldap.configure_user_search_strategy false - assert_equal GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy.class + @ldap.configure_user_search_strategy("default") + assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy end def test_capabilities From 5f9e3d7f03eae3ccf902f8f5590bfddc5ebdd253 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:14:07 -0500 Subject: [PATCH 223/307] Make active_directory_capability? private again --- lib/github/ldap.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 7ec20cd..4e5fe97 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -335,6 +335,9 @@ def configure_member_search_strategy(strategy = nil) end end + + private + # Internal: Detect whether the LDAP host is an ActiveDirectory server. # # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. @@ -344,8 +347,6 @@ def active_directory_capability? capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end - private - attr_reader :admin_user, :admin_password end end From 0ea4a93851bd8f96db1269c50a6eb4f048422620 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:14:45 -0500 Subject: [PATCH 224/307] Use mock utility for mock objects; Don't stub :new on Net::LDAP --- test/user_search/active_directory_test.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index 7015686..13e8123 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -9,26 +9,26 @@ def setup end def test_global_catalog_returns_empty_array_for_no_results - mock_global_catalog_connection = Object.new + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") mock_global_catalog_connection.expects(:search).returns(nil) - Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [], results end def test_global_catalog_returns_array_of_results - mock_global_catalog_connection = Object.new - stub_entry = Object.new + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") + stub_entry = mock("Net::LDAP::Entry") mock_global_catalog_connection.expects(:search).returns(stub_entry) - Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [stub_entry], results end def test_searches_with_empty_base_dn - mock_global_catalog_connection = Object.new + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") mock_global_catalog_connection.expects(:search).with(has_entry(:base => "")) - Net::LDAP.expects(:new).returns(mock_global_catalog_connection) + @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) @ad_user_search.perform("login", "CN=Joe", "uid", {}) end From 18eb39925e392f5ad824faae92cb0b45243af48f Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:15:40 -0500 Subject: [PATCH 225/307] Test auth on user search strategy; minor test cleanup --- test/user_search/active_directory_test.rb | 2 ++ test/user_search/default_test.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index 13e8123..ae23146 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -38,6 +38,8 @@ def test_global_catalog_default_settings auth = global_catalog.instance_variable_get(:@auth) assert_equal :simple, auth[:method] + assert_equal "uid=admin,dc=github,dc=com", auth[:username] + assert_equal "passworD1", auth[:password] assert_equal "127.0.0.1", global_catalog.host assert_equal 3268, global_catalog.port assert_equal "MockInstrumentationService", instrumentation_service.class.name diff --git a/test/user_search/default_test.rb b/test/user_search/default_test.rb index 939e7ff..6913d0c 100644 --- a/test/user_search/default_test.rb +++ b/test/user_search/default_test.rb @@ -15,6 +15,6 @@ def test_default_search_options base: "CN=HI,CN=McDunnough", filter: kind_of(Net::LDAP::Filter) )) - results = @default_user_search.perform("","CN=HI,CN=McDunnough","",{}) + @default_user_search.perform("","CN=HI,CN=McDunnough","",{}) end end From 32222d596b7c1ac2deae4c55b2dec9e1fc9aa5df Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:26:55 -0500 Subject: [PATCH 226/307] Updated documentation --- lib/github/ldap.rb | 14 +++++++++++++- lib/github/ldap/user_search/default.rb | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 4e5fe97..7cc4ca3 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -204,7 +204,7 @@ def search(options, &block) end end - # Public: Searches the host LDAP server's Root DSE for capabilities and + # Internal: Searches the host LDAP server's Root DSE for capabilities and # extensions. # # Returns a Net::LDAP::Entry object. @@ -292,6 +292,18 @@ def configure_membership_validation_strategy(strategy = nil) end end + # Internal: Set the user search strategy that will be used by + # Domain#user?. + # + # strategy - Can be either 'default' or 'global_catalog'. + # 'default' strategy will search the configured + # domain controller with a search base relative + # to the controller's domain context. + # 'global_catalog' will search the entire forest + # using Active Directory's Global Catalog + # functionality. If the server is not an Active + # Directory, will use the default strategy regardless + # of the requested configuration. def configure_user_search_strategy(strategy) @user_search_strategy = begin case strategy.to_s diff --git a/lib/github/ldap/user_search/default.rb b/lib/github/ldap/user_search/default.rb index 88f723b..d058ee6 100644 --- a/lib/github/ldap/user_search/default.rb +++ b/lib/github/ldap/user_search/default.rb @@ -1,6 +1,9 @@ module GitHub class Ldap module UserSearch + # The default user search strategy, mainly for allowing Domain#user? to + # search for a user on the configured domain controller, or use the Global + # Catalog to search across the entire Active Directory forest. class Default include Filter @@ -12,12 +15,17 @@ def initialize(ldap) @options[:size] = 1 end + # Performs a normal search on the configured domain controller + # using the default base DN, uid, search_options def perform(login, base_name, uid, search_options) search_options[:filter] = login_filter(uid, login) search_options[:base] = base_name search(options.merge(search_options)) end + # The default search. This can be overridden by a child class + # like GitHub::Ldap::UserSearch::ActiveDirectory to change the + # scope of the search. def search(options) ldap.search(options) end From 50120001ab73aeb17733107e7189c8c83ed20daa Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:37:12 -0500 Subject: [PATCH 227/307] Explictly override options for setting the base DN to "" --- lib/github/ldap/user_search/active_directory.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index 27e2231..133eb6a 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -6,8 +6,6 @@ class ActiveDirectory < Default # Public - Overridden from base class to set the base to "", and use the # Global Catalog to perform the user search. def search(search_options) - # when doing a global search for a user's DN, set the search base to blank - options[:base] = "" Array(global_catalog_connection.search(search_options.merge(options))) end @@ -19,6 +17,11 @@ def search(search_options) def global_catalog_connection GlobalCatalog.connection(ldap) end + + # When doing a global search for a user's DN, set the search base to blank + def options + super.merge(base: "") + end end class GlobalCatalog < Net::LDAP From a52264105004cc706c53bf4e58062dea8026db5c Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:39:37 -0500 Subject: [PATCH 228/307] better hash structure --- lib/github/ldap/user_search/default.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/github/ldap/user_search/default.rb b/lib/github/ldap/user_search/default.rb index d058ee6..2f1aa3f 100644 --- a/lib/github/ldap/user_search/default.rb +++ b/lib/github/ldap/user_search/default.rb @@ -9,10 +9,11 @@ class Default def initialize(ldap) @ldap = ldap - @options = {} - @options[:attributes] = [] - @options[:paged_searches_supported] = true - @options[:size] = 1 + @options = { + :attributes => [], + :paged_searches_supported => true, + :size => 1 + } end # Performs a normal search on the configured domain controller From a3163fe2ede5ae58469d9b54dd1477b0fb531299 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:41:34 -0500 Subject: [PATCH 229/307] Updated documentation --- lib/github/ldap/user_search/active_directory.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index 133eb6a..fcf059a 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -9,8 +9,7 @@ def search(search_options) Array(global_catalog_connection.search(search_options.merge(options))) end - # Returns a memoized connection to an Active Directory Global Catalog - # if the server is an Active Directory instance, otherwise returns nil. + # Returns a connection to the Active Directory Global Catalog # # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx # From c3ac0b32b1db953cec18f2668ef715473ab67328 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:53:07 -0500 Subject: [PATCH 230/307] Don't fall back on default user search for non-AD controllers --- lib/github/ldap.rb | 6 +----- test/ldap_test.rb | 5 +++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 7cc4ca3..e2b361b 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -310,11 +310,7 @@ def configure_user_search_strategy(strategy) when "default" GitHub::Ldap::UserSearch::Default.new(self) when "global_catalog" - if active_directory_capability? - GitHub::Ldap::UserSearch::ActiveDirectory.new(self) - else - GitHub::Ldap::UserSearch::Default.new(self) - end + GitHub::Ldap::UserSearch::ActiveDirectory.new(self) else GitHub::Ldap::UserSearch::Default.new(self) end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 0af1cb4..2bbe221 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -121,14 +121,15 @@ def test_user_search_strategy_global_catalog_when_configured_and_has_capability assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy end - def test_user_search_strategy_not_active_directory_without_capability + def test_user_search_strategy_is_active_directory_even_without_capability @ldap.stubs(:active_directory_capability?).returns(false) @ldap.configure_user_search_strategy("global_catalog") - assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy + assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy end def test_user_search_strategy_default_when_configured @ldap.configure_user_search_strategy("default") + refute_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy end From 4154214aefbcdf065da85bc56b8a8628277a74ff Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 11:56:45 -0500 Subject: [PATCH 231/307] Make search & options private on ActiveDirectory user search --- lib/github/ldap/user_search/active_directory.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index fcf059a..a76a6bf 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -3,12 +3,6 @@ class Ldap module UserSearch class ActiveDirectory < Default - # Public - Overridden from base class to set the base to "", and use the - # Global Catalog to perform the user search. - def search(search_options) - Array(global_catalog_connection.search(search_options.merge(options))) - end - # Returns a connection to the Active Directory Global Catalog # # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx @@ -17,6 +11,14 @@ def global_catalog_connection GlobalCatalog.connection(ldap) end + private + + # Private - Overridden from base class to set the base to "", and use the + # Global Catalog to perform the user search. + def search(search_options) + Array(global_catalog_connection.search(search_options.merge(options))) + end + # When doing a global search for a user's DN, set the search base to blank def options super.merge(base: "") From 3da33e4fa099d2937fce3d4d705cbe7dbcaf908d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 12:02:39 -0500 Subject: [PATCH 232/307] Make global connection interface private --- lib/github/ldap/user_search/active_directory.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index a76a6bf..d2e0981 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -3,14 +3,6 @@ class Ldap module UserSearch class ActiveDirectory < Default - # Returns a connection to the Active Directory Global Catalog - # - # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx - # - def global_catalog_connection - GlobalCatalog.connection(ldap) - end - private # Private - Overridden from base class to set the base to "", and use the @@ -19,6 +11,10 @@ def search(search_options) Array(global_catalog_connection.search(search_options.merge(options))) end + def global_catalog_connection + GlobalCatalog.connection(ldap) + end + # When doing a global search for a user's DN, set the search base to blank def options super.merge(base: "") @@ -29,6 +25,10 @@ class GlobalCatalog < Net::LDAP STANDARD_GC_PORT = 3268 LDAPS_GC_PORT = 3269 + # Returns a connection to the Active Directory Global Catalog + # + # See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx + # def self.connection(ldap) @global_catalog_instance ||= begin netldap = ldap.connection From 735a42b2cd2d2d43b76b4fb83cfd90dec60f4a27 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 12:04:03 -0500 Subject: [PATCH 233/307] update documentation --- lib/github/ldap.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index e2b361b..bdcd748 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -301,9 +301,7 @@ def configure_membership_validation_strategy(strategy = nil) # to the controller's domain context. # 'global_catalog' will search the entire forest # using Active Directory's Global Catalog - # functionality. If the server is not an Active - # Directory, will use the default strategy regardless - # of the requested configuration. + # functionality. def configure_user_search_strategy(strategy) @user_search_strategy = begin case strategy.to_s From 3bc3979429160847dd16ae6d92bf5eb096aff6d9 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 12:06:50 -0500 Subject: [PATCH 234/307] Remove unneded test & condition --- test/ldap_test.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 2bbe221..2d47fac 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -115,14 +115,7 @@ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_de assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy end - def test_user_search_strategy_global_catalog_when_configured_and_has_capability - @ldap.stubs(:active_directory_capability?).returns(true) - @ldap.configure_user_search_strategy("global_catalog") - assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy - end - - def test_user_search_strategy_is_active_directory_even_without_capability - @ldap.stubs(:active_directory_capability?).returns(false) + def test_user_search_strategy_global_catalog_when_configured @ldap.configure_user_search_strategy("global_catalog") assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy end From c05c4d73fb8efc106e2730441cef8dc3b931dd85 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Tue, 26 Jul 2016 17:54:04 -0500 Subject: [PATCH 235/307] First draft of referral chasing: set up referral connection properties --- .../membership_validators/active_directory.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 69b9bc8..cfca450 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -24,11 +24,24 @@ def perform(entry) # Sets the entry to the base and scopes the search to the base, # according to the source documentation, found here: # http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx - matched = ldap.search \ - filter: membership_in_chain_filter(entry), + + filter = membership_in_chain_filter(entry) + options = { + filter: filter, base: entry.dn, scope: Net::LDAP::SearchScope_BaseObject, + return_referrals: true, attributes: ATTRS + } + + referral_entries = [] + matched = ldap.search(options) do |ref| + referral_entries << ref + end + + unless !matched.blank? || referral_entries.blank? + matched = chase_referral(referral_entries) + end # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive From 9afe1646fef081392687f26eaab13d471e6e5a2e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Tue, 26 Jul 2016 17:54:53 -0500 Subject: [PATCH 236/307] WIP: Added referral chasing method, will move to GitHub::Ldap --- .../membership_validators/active_directory.rb | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index cfca450..022e0a2 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -45,7 +45,29 @@ def perform(entry) # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive - matched.map { |m| m.dn.downcase }.include?(entry.dn.downcase) + Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) + end + + def chase_referral(referral_entries) + referral = referral_entries.first + uri = URI(referral[:search_referrals].first) + + new_filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local)" + new_auth = {:method=>:simple, :username=>"CN=Administrator,CN=Users,DC=dc4,DC=ghe,DC=local", :password=>"vagrant"} + + new_base = URI.unescape(uri.path.sub(/^\//, '')) + + referral_connection = Net::LDAP.new({ + host: uri.host, + port: uri.port, + filter: new_filter, + base: new_base, + auth: new_auth, + instrumentation_service: ldap.instance_variable_get(:@instrumentation_service) + }) + + referral_connection.bind + referral_connection.search end # Internal: Constructs a membership filter using the "in chain" From 1fce71769267ccaff15621404df2143c471036d7 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 27 Jul 2016 23:16:09 -0500 Subject: [PATCH 237/307] Moved chase_referral to ldap class --- ldap.sh | 9 +++++++++ lib/github/ldap.rb | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 ldap.sh diff --git a/ldap.sh b/ldap.sh new file mode 100644 index 0000000..32a654c --- /dev/null +++ b/ldap.sh @@ -0,0 +1,9 @@ + ldapsearch -x -h dc4.ghe.local \ + -b "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local" \ + -D "GHE\Administrator" -w "vagrant" \ + "(|(|(| \ + (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-users,CN=Users,DC=ghe,DC=local) \ + (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-users,CN=Users, DC=dc4,DC=ghe,DC=local)) \ + (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=ghe,DC=local)) \ + (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local))" + diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index bdcd748..24c3346 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -292,6 +292,7 @@ def configure_membership_validation_strategy(strategy = nil) end end +<<<<<<< 9afe1646fef081392687f26eaab13d471e6e5a2e # Internal: Set the user search strategy that will be used by # Domain#user?. # @@ -313,6 +314,27 @@ def configure_user_search_strategy(strategy) GitHub::Ldap::UserSearch::Default.new(self) end end +======= + def chase_referral(referral_entries, filter) + referral = referral_entries.first + uri = URI(referral[:search_referrals].first) + + #new_filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local)" + + new_base = URI.unescape(uri.path.sub(/^\//, '')) + options = { + filter: filter, + base: new_base, + instrumentation_service: instrumentation_service, + scope: Net::LDAP::SearchScope_BaseObject, + attributes: ["dn"] + } + + results = [] + referral_connection = get_connection_by_host(uri.host) + puts "OPTIONS #{options}" + referral_connection.search(options) +>>>>>>> Moved chase_referral to ldap class end # Internal: Configure the member search strategy. From 526d63ac3991ed01822390b34ff77600d3604e45 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 27 Jul 2016 23:16:38 -0500 Subject: [PATCH 238/307] Cache new referral connections as we go --- lib/github/ldap.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 24c3346..fc4eff1 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -337,6 +337,21 @@ def chase_referral(referral_entries, filter) >>>>>>> Moved chase_referral to ldap class end + def get_connection_by_host(host, port=389, auth=nil) + auth ||= {:method => :simple, :username => @admin_user, :password => @admin_password} + + @connections ||= Hash.new do |cache, host| + conn = Net::LDAP.new({ + host: host, + port: port, + auth: auth + }) + conn.authenticate(auth[:username], auth[:password]) + cache[host] = conn + end + @connections[host] + end + # Internal: Configure the member search strategy. # # From 82704e5a13fbdae9e3b42ad86ab7d87a25d5a4a4 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 27 Jul 2016 23:20:23 -0500 Subject: [PATCH 239/307] removed old chase_referral method --- .../membership_validators/active_directory.rb | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 022e0a2..e6b40d8 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -48,26 +48,6 @@ def perform(entry) Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) end - def chase_referral(referral_entries) - referral = referral_entries.first - uri = URI(referral[:search_referrals].first) - - new_filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local)" - new_auth = {:method=>:simple, :username=>"CN=Administrator,CN=Users,DC=dc4,DC=ghe,DC=local", :password=>"vagrant"} - - new_base = URI.unescape(uri.path.sub(/^\//, '')) - - referral_connection = Net::LDAP.new({ - host: uri.host, - port: uri.port, - filter: new_filter, - base: new_base, - auth: new_auth, - instrumentation_service: ldap.instance_variable_get(:@instrumentation_service) - }) - - referral_connection.bind - referral_connection.search end # Internal: Constructs a membership filter using the "in chain" From 022d455f5061a1409ac5eb7a2d8a3a3f31800946 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 27 Jul 2016 23:20:59 -0500 Subject: [PATCH 240/307] Add method to reset base dn on search filter for use with referrals --- lib/github/ldap/membership_validators/active_directory.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index e6b40d8..4ac7a4d 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -3,6 +3,7 @@ class Ldap module MembershipValidators ATTRS = %w(dn) OID = "1.2.840.113556.1.4.1941" + DN_BASE_MATCHER = /DC=.*/ # Validates membership using the ActiveDirectory "in chain" matching rule. # @@ -48,6 +49,9 @@ def perform(entry) Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) end + def set_new_base_dn(filter, entry) + base_dn = DN_BASE_MATCHER.match(entry.dn)[0] + filter.to_s.sub(DN_BASE_MATCHER, base_dn) end # Internal: Constructs a membership filter using the "in chain" From 652633be0edc232d04301fce09eecd3be1b19778 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Wed, 27 Jul 2016 23:21:37 -0500 Subject: [PATCH 241/307] Reset base dn on filter before searcing on referral controller --- lib/github/ldap/membership_validators/active_directory.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 4ac7a4d..b73b129 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -41,7 +41,8 @@ def perform(entry) end unless !matched.blank? || referral_entries.blank? - matched = chase_referral(referral_entries) + filter = set_new_base_dn(filter, entry) + matched = ldap.chase_referral(referral_entries, filter) end # membership validated if entry was matched and returned as a result From bb149caad12d099257f8ab8692d36dd131bbf9e3 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 28 Jul 2016 22:41:27 -0500 Subject: [PATCH 242/307] cleanup --- lib/github/ldap.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index fc4eff1..cfe0d43 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -319,8 +319,6 @@ def chase_referral(referral_entries, filter) referral = referral_entries.first uri = URI(referral[:search_referrals].first) - #new_filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local)" - new_base = URI.unescape(uri.path.sub(/^\//, '')) options = { filter: filter, @@ -332,7 +330,6 @@ def chase_referral(referral_entries, filter) results = [] referral_connection = get_connection_by_host(uri.host) - puts "OPTIONS #{options}" referral_connection.search(options) >>>>>>> Moved chase_referral to ldap class end From 218675bd6d21ac444b96e5a3db5c1594b40692b9 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 28 Jul 2016 22:41:41 -0500 Subject: [PATCH 243/307] Use GitHub::Ldap instead of Net::LDAP for referral connections --- lib/github/ldap.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index cfe0d43..e1d42f7 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -338,12 +338,11 @@ def get_connection_by_host(host, port=389, auth=nil) auth ||= {:method => :simple, :username => @admin_user, :password => @admin_password} @connections ||= Hash.new do |cache, host| - conn = Net::LDAP.new({ + conn = GitHub::Ldap.new({ host: host, port: port, auth: auth }) - conn.authenticate(auth[:username], auth[:password]) cache[host] = conn end @connections[host] From 971698c9bd5cd4f754e06b4ab478d5761a4d4a95 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 28 Jul 2016 22:42:56 -0500 Subject: [PATCH 244/307] Don't reset base_dn, will pass in FQDN for groups from client --- .../ldap/membership_validators/active_directory.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index b73b129..def44a9 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -40,19 +40,15 @@ def perform(entry) referral_entries << ref end - unless !matched.blank? || referral_entries.blank? - filter = set_new_base_dn(filter, entry) + if referral_entries.present? matched = ldap.chase_referral(referral_entries, filter) end # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive - Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) - end - def set_new_base_dn(filter, entry) - base_dn = DN_BASE_MATCHER.match(entry.dn)[0] - filter.to_s.sub(DN_BASE_MATCHER, base_dn) + result = Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) + result end # Internal: Constructs a membership filter using the "in chain" From 990cca8f6129b2df21aa9f5207b1c7780cf0179e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 28 Jul 2016 22:44:12 -0500 Subject: [PATCH 245/307] No need for DN matcher --- lib/github/ldap/membership_validators/active_directory.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index def44a9..ed336d7 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -3,7 +3,6 @@ class Ldap module MembershipValidators ATTRS = %w(dn) OID = "1.2.840.113556.1.4.1941" - DN_BASE_MATCHER = /DC=.*/ # Validates membership using the ActiveDirectory "in chain" matching rule. # From 54defdb2b7777ea178ef853f9a6163e38bf46716 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 13:24:57 -0500 Subject: [PATCH 246/307] Create a ConnectionPool object to encapsulate caching connection objects --- lib/github/ldap/connection_pool.rb | 24 ++++++++++++++++++++++++ test/connection_pool_test.rb | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 lib/github/ldap/connection_pool.rb create mode 100644 test/connection_pool_test.rb diff --git a/lib/github/ldap/connection_pool.rb b/lib/github/ldap/connection_pool.rb new file mode 100644 index 0000000..c3c723c --- /dev/null +++ b/lib/github/ldap/connection_pool.rb @@ -0,0 +1,24 @@ +module GitHub + class Ldap + class ConnectionPool + + # Public - Create or return cached instance of GitHub::Ldap created with options, + # where the cache key is the value of options[:host]. + # + # Returns an instance of GitHub::Ldap + def self.get_connection(options={}) + @instance ||= self.new + @instance.get_connection(options) + end + + def get_connection(options) + host = options[:host] + @connections ||= Hash.new do |cache, host| + conn = GitHub::Ldap.new(options) + cache[host] = conn + end + @connections[host] + end + end + end +end diff --git a/test/connection_pool_test.rb b/test/connection_pool_test.rb new file mode 100644 index 0000000..de70be1 --- /dev/null +++ b/test/connection_pool_test.rb @@ -0,0 +1,21 @@ +require_relative 'test_helper' +require 'mocha/mini_test' + +class GitHubLdapConnectionPoolTestCases < GitHub::Ldap::Test + + def test_get_connection + conn = GitHub::Ldap::ConnectionPool.get_connection({:host => "host"}) + assert_equal GitHub::Ldap, conn.class + end + + def test_returns_cached_connection + conn1 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) + conn2 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) + assert_equal conn1.object_id, conn2.object_id + + conn3 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) + conn4 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) + refute_equal conn1.object_id, conn3.object_id + assert_equal conn3.object_id, conn4.object_id + end +end From 4b0ccff95a8a4932cd0326ff784bb4673dcef4b8 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 13:28:39 -0500 Subject: [PATCH 247/307] Split connection pool tests --- test/connection_pool_test.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/connection_pool_test.rb b/test/connection_pool_test.rb index de70be1..0f65e18 100644 --- a/test/connection_pool_test.rb +++ b/test/connection_pool_test.rb @@ -12,10 +12,13 @@ def test_returns_cached_connection conn1 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) conn2 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) assert_equal conn1.object_id, conn2.object_id + end + def test_creates_new_connections_per_host + conn1 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) + conn2 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) conn3 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) - conn4 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) - refute_equal conn1.object_id, conn3.object_id - assert_equal conn3.object_id, conn4.object_id + refute_equal conn1.object_id, conn2.object_id + assert_equal conn2.object_id, conn3.object_id end end From edd8107d2397cec32b59e0c5d53788814c7999ec Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 16:27:18 -0500 Subject: [PATCH 248/307] A little cleanup --- lib/github/ldap.rb | 20 +------------------- test/connection_pool_test.rb | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index e1d42f7..21660c8 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -12,6 +12,7 @@ require 'github/ldap/membership_validators' require 'github/ldap/user_search/default' require 'github/ldap/user_search/active_directory' +require 'github/ldap/connection_pool' module GitHub class Ldap @@ -292,7 +293,6 @@ def configure_membership_validation_strategy(strategy = nil) end end -<<<<<<< 9afe1646fef081392687f26eaab13d471e6e5a2e # Internal: Set the user search strategy that will be used by # Domain#user?. # @@ -314,24 +314,6 @@ def configure_user_search_strategy(strategy) GitHub::Ldap::UserSearch::Default.new(self) end end -======= - def chase_referral(referral_entries, filter) - referral = referral_entries.first - uri = URI(referral[:search_referrals].first) - - new_base = URI.unescape(uri.path.sub(/^\//, '')) - options = { - filter: filter, - base: new_base, - instrumentation_service: instrumentation_service, - scope: Net::LDAP::SearchScope_BaseObject, - attributes: ["dn"] - } - - results = [] - referral_connection = get_connection_by_host(uri.host) - referral_connection.search(options) ->>>>>>> Moved chase_referral to ldap class end def get_connection_by_host(host, port=389, auth=nil) diff --git a/test/connection_pool_test.rb b/test/connection_pool_test.rb index 0f65e18..15f5fd2 100644 --- a/test/connection_pool_test.rb +++ b/test/connection_pool_test.rb @@ -1,5 +1,4 @@ require_relative 'test_helper' -require 'mocha/mini_test' class GitHubLdapConnectionPoolTestCases < GitHub::Ldap::Test From 5d7c294578d68a8ae76394c9881137d25e8e6303 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 16:27:39 -0500 Subject: [PATCH 249/307] Abstracted referral chasing into its own class --- lib/github/ldap/referral_chaser.rb | 41 ++++++++++++++++++++++++++ test/referral_chaser_test.rb | 47 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 lib/github/ldap/referral_chaser.rb create mode 100644 test/referral_chaser_test.rb diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb new file mode 100644 index 0000000..a4289b2 --- /dev/null +++ b/lib/github/ldap/referral_chaser.rb @@ -0,0 +1,41 @@ +module GitHub + class Ldap + class ReferralChaser + + def initialize(referral_entries, admin_user, admin_password) + @referral_entries = referral_entries + @admin_user = admin_user + @admin_password = admin_password + end + + def with_referrals + referral_entries.each do |entry| + entry[:search_referrals].each do |referral_string| + yield(Referral.new(referral_string, admin_user, admin_password)) + end + end + end + + private + + attr_reader :referral_entries, :admin_user, :admin_password + + class Referral + def initialize(referral_string, admin_user, admin_password) + uri = URI(referral_string) + @search_base = URI.unescape(uri.path.sub(/^\//, '')) + + connection_options = { + host: uri.host, + admin_user: admin_user, + admin_password: admin_password + } + + @connection = GitHub::Ldap::ConnectionPool.get_connection(connection_options) + end + + attr_reader :search_base, :connection + end + end + end +end diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb new file mode 100644 index 0000000..823b7d7 --- /dev/null +++ b/test/referral_chaser_test.rb @@ -0,0 +1,47 @@ +require_relative 'test_helper' + +class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test + + def setup + referral_entries = [ + {:search_referrals => ["ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]} + ] + @chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + end + + def test_creates_connection + @chaser.with_referrals do |referral| + assert_equal GitHub::Ldap, referral.connection.class + end + end + + def test_returns_url_escaped_search_base + @chaser.with_referrals do |referral| + assert_equal "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local", referral.search_base + end + end + + def test_executes_for_every_entry + referral_entries = [ + {:search_referrals => ["test1"]}, + {:search_referrals => ["test2"]} + ] + chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + + called = 0 + chaser.with_referrals { called += 1 } + assert_equal 2, called + end + + def test_executes_for_every_referral + referral_entries = [ + {:search_referrals => ["test1"]}, + {:search_referrals => ["test2", "test3"]} + ] + chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + + called = 0 + chaser.with_referrals { called += 1 } + assert_equal 3, called + end +end From 96ba488c1e07d7a1f95f1287414805c3b0493b4c Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 16:28:24 -0500 Subject: [PATCH 250/307] load referral_chaser; exposed admin user & pw for referrals to use --- lib/github/ldap.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 21660c8..31247ba 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -13,6 +13,7 @@ require 'github/ldap/user_search/default' require 'github/ldap/user_search/active_directory' require 'github/ldap/connection_pool' +require 'github/ldap/referral_chaser' module GitHub class Ldap @@ -47,7 +48,9 @@ class Ldap :member_search_strategy, :instrumentation_service, :user_search_strategy, - :connection + :connection, + :admin_user, + :admin_password # Build a new GitHub::Ldap instance # From 9edc1c537408f9e90895b19f11b2c71d16dda63f Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 16:29:21 -0500 Subject: [PATCH 251/307] Removed dead code --- lib/github/ldap.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 31247ba..4d8bc99 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -296,6 +296,7 @@ def configure_membership_validation_strategy(strategy = nil) end end +<<<<<<< 96ba488c1e07d7a1f95f1287414805c3b0493b4c # Internal: Set the user search strategy that will be used by # Domain#user?. # From 7f3e6f2ca6d7c8ab670a78fd8cfa616c14b9dbc4 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 29 Jul 2016 16:30:05 -0500 Subject: [PATCH 252/307] Use new referral chaser class in ActiveDirectory validator --- lib/github/ldap/membership_validators/active_directory.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index ed336d7..6dae2e4 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -40,14 +40,17 @@ def perform(entry) end if referral_entries.present? - matched = ldap.chase_referral(referral_entries, filter) + chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, ldap.admin_user, ldap.admin_password) + chaser.with_referrals do |referral| + options[:base] = referral.search_base + matched = referral.connection.search(options) + end end # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive result = Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) - result end # Internal: Constructs a membership filter using the "in chain" From b7da6e7605b2034da638cc192fa3d81b3af78a56 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Sat, 30 Jul 2016 13:09:45 -0500 Subject: [PATCH 253/307] Pushing referral aggregation from callsite into ReferallChaser --- lib/github/ldap/referral_chaser.rb | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index a4289b2..68371b7 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -2,23 +2,33 @@ module GitHub class Ldap class ReferralChaser - def initialize(referral_entries, admin_user, admin_password) - @referral_entries = referral_entries - @admin_user = admin_user - @admin_password = admin_password + def initialize(connection) + @connection = connection + @admin_user = connection.admin_user + @admin_password = connection.admin_password end - def with_referrals + def search(options) + search_results = [] + referral_entries = [] + + search_results = connection.search(options) do |referral_entry| + referral_entries << referral_entry + end + referral_entries.each do |entry| entry[:search_referrals].each do |referral_string| - yield(Referral.new(referral_string, admin_user, admin_password)) + referral = Referral.new(referral_string, admin_user, admin_password) + search_results.concat(referral.search(options)) end end + + search_results end private - attr_reader :referral_entries, :admin_user, :admin_password + attr_reader :connection, :admin_user, :admin_password class Referral def initialize(referral_string, admin_user, admin_password) From 704ea393656354d82302103dce64a2b7342b5649 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:28:58 -0500 Subject: [PATCH 254/307] Use base connection's port as the default port --- lib/github/ldap.rb | 6 +++++- lib/github/ldap/connection_cache.rb | 26 ++++++++++++++++++++++++++ lib/github/ldap/connection_pool.rb | 24 ------------------------ lib/github/ldap/referral_chaser.rb | 29 ++++++++++++++++++----------- test/connection_cache_test.rb | 23 +++++++++++++++++++++++ test/connection_pool_test.rb | 23 ----------------------- 6 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 lib/github/ldap/connection_cache.rb delete mode 100644 lib/github/ldap/connection_pool.rb create mode 100644 test/connection_cache_test.rb delete mode 100644 test/connection_pool_test.rb diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 4d8bc99..09df551 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -13,7 +13,9 @@ require 'github/ldap/user_search/default' require 'github/ldap/user_search/active_directory' require 'github/ldap/connection_pool' +require 'github/ldap/connection_cache' require 'github/ldap/referral_chaser' +require 'github/ldap/url' module GitHub class Ldap @@ -50,7 +52,8 @@ class Ldap :user_search_strategy, :connection, :admin_user, - :admin_password + :admin_password, + :port # Build a new GitHub::Ldap instance # @@ -80,6 +83,7 @@ def initialize(options = {}) # Keep a reference to these as default auth for a Global Catalog if needed @admin_user = options[:admin_user] @admin_password = options[:admin_password] + @port = options[:port] @connection = Net::LDAP.new({ host: options[:host], diff --git a/lib/github/ldap/connection_cache.rb b/lib/github/ldap/connection_cache.rb new file mode 100644 index 0000000..d2feab9 --- /dev/null +++ b/lib/github/ldap/connection_cache.rb @@ -0,0 +1,26 @@ +module GitHub + class Ldap + + # A simple cache of GitHub::Ldap objects to prevent creating multiple + # instances of connections that point to the same URI/host. + class ConnectionCache + + # Public - Create or return cached instance of GitHub::Ldap created with options, + # where the cache key is the value of options[:host]. + # + # options - Initialization attributes suitable for creating a new connection with + # GitHub::Ldap.new(options) + # + # Returns true or false. + def self.get_connection(options={}) + @cache ||= self.new + @cache.get_connection(options) + end + + def get_connection(options) + @connections ||= {} + @connections[options[:host]] ||= GitHub::Ldap.new(options) + end + end + end +end diff --git a/lib/github/ldap/connection_pool.rb b/lib/github/ldap/connection_pool.rb deleted file mode 100644 index c3c723c..0000000 --- a/lib/github/ldap/connection_pool.rb +++ /dev/null @@ -1,24 +0,0 @@ -module GitHub - class Ldap - class ConnectionPool - - # Public - Create or return cached instance of GitHub::Ldap created with options, - # where the cache key is the value of options[:host]. - # - # Returns an instance of GitHub::Ldap - def self.get_connection(options={}) - @instance ||= self.new - @instance.get_connection(options) - end - - def get_connection(options) - host = options[:host] - @connections ||= Hash.new do |cache, host| - conn = GitHub::Ldap.new(options) - cache[host] = conn - end - @connections[host] - end - end - end -end diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index 68371b7..90857d2 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -16,11 +16,11 @@ def search(options) referral_entries << referral_entry end - referral_entries.each do |entry| - entry[:search_referrals].each do |referral_string| - referral = Referral.new(referral_string, admin_user, admin_password) - search_results.concat(referral.search(options)) - end + unless referral_entries.empty? + entry = referral_entries.first + referral_string = entry[:search_referrals].first + referral = Referral.new(referral_string, admin_user, admin_password, port) + search_results = referral.search(options) end search_results @@ -28,20 +28,27 @@ def search(options) private - attr_reader :connection, :admin_user, :admin_password + attr_reader :connection, :admin_user, :admin_password, :port + # Represents a referral entry from an LDAP search result. Constructs a corresponding + # GitHub::Ldap object from the paramaters on the referral_url and provides a #search + # method to continue the search on the referred domain. class Referral - def initialize(referral_string, admin_user, admin_password) - uri = URI(referral_string) - @search_base = URI.unescape(uri.path.sub(/^\//, '')) + def initialize(referral_url, admin_user, admin_password, port=nil) + url = GitHub::Ldap::URL.new(referral_url) + @search_base = url.dn connection_options = { - host: uri.host, + host: url.host, + port: port || url.port, + scope: url.scope, admin_user: admin_user, admin_password: admin_password } - @connection = GitHub::Ldap::ConnectionPool.get_connection(connection_options) + @connection = GitHub::Ldap::ConnectionCache.get_connection(connection_options) + end + end attr_reader :search_base, :connection diff --git a/test/connection_cache_test.rb b/test/connection_cache_test.rb new file mode 100644 index 0000000..7eb0aa3 --- /dev/null +++ b/test/connection_cache_test.rb @@ -0,0 +1,23 @@ +require_relative 'test_helper' + +class GitHubLdapConnectionCacheTestCases < GitHub::Ldap::Test + + def test_get_connection + conn = GitHub::Ldap::ConnectionCache.get_connection({:host => "host"}) + assert_equal GitHub::Ldap, conn.class + end + + def test_returns_cached_connection + conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) + conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) + assert_equal conn1.object_id, conn2.object_id + end + + def test_creates_new_connections_per_host + conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) + conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) + conn3 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) + refute_equal conn1.object_id, conn2.object_id + assert_equal conn2.object_id, conn3.object_id + end +end diff --git a/test/connection_pool_test.rb b/test/connection_pool_test.rb deleted file mode 100644 index 15f5fd2..0000000 --- a/test/connection_pool_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -require_relative 'test_helper' - -class GitHubLdapConnectionPoolTestCases < GitHub::Ldap::Test - - def test_get_connection - conn = GitHub::Ldap::ConnectionPool.get_connection({:host => "host"}) - assert_equal GitHub::Ldap, conn.class - end - - def test_returns_cached_connection - conn1 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) - conn2 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) - assert_equal conn1.object_id, conn2.object_id - end - - def test_creates_new_connections_per_host - conn1 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.local"}) - conn2 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) - conn3 = GitHub::Ldap::ConnectionPool.get_connection({:host => "ghe.dev"}) - refute_equal conn1.object_id, conn2.object_id - assert_equal conn2.object_id, conn3.object_id - end -end From 819955525fb8cf48d9ba83d0978fe0b619feeb43 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:39:41 -0500 Subject: [PATCH 255/307] Use ReferralChaser to do all the search heavy lifting for AD --- .../membership_validators/active_directory.rb | 16 ++++------------ lib/github/ldap/referral_chaser.rb | 4 ++++ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 6dae2e4..8be26bd 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -34,18 +34,10 @@ def perform(entry) attributes: ATTRS } - referral_entries = [] - matched = ldap.search(options) do |ref| - referral_entries << ref - end - - if referral_entries.present? - chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, ldap.admin_user, ldap.admin_password) - chaser.with_referrals do |referral| - options[:base] = referral.search_base - matched = referral.connection.search(options) - end - end + # Chase any potential referrals for an entry that may be owned by a different + # domain controller. + chaser = GitHub::Ldap::ReferralChaser.new(ldap) + matched = chaser.search(options) # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index 90857d2..c37c335 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -49,6 +49,10 @@ def initialize(referral_url, admin_user, admin_password, port=nil) @connection = GitHub::Ldap::ConnectionCache.get_connection(connection_options) end + # Search the referred domain controller with options, merging in the referred search + # base DN onto options[:base]. + def search(options) + connection.search(options.merge(base: search_base)) end attr_reader :search_base, :connection From d3dc5c413195a7ba4115612ee95d7a2d258fe745 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:40:38 -0500 Subject: [PATCH 256/307] Updated documentation --- lib/github/ldap/referral_chaser.rb | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index c37c335..1e0b551 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -1,13 +1,45 @@ module GitHub class Ldap + + # This class adds referral chasing capability to a GitHub::Ldap connection. + # + # See: https://technet.microsoft.com/en-us/library/cc978014.aspx + # http://www.umich.edu/~dirsvcs/ldap/doc/other/ldap-ref.html + # class ReferralChaser + # Public - Creates a ReferralChaser that decorates an instance of GitHub::Ldap + # with additional functionality to the #search method, allowing it to chase + # any referral entries and aggregate the results into a single response. + # + # connection - The instance of GitHub::Ldap to use for searching. Will use + # the connection's authentication, (admin_user and admin_password) as credentials + # for connecting to referred domain controllers. def initialize(connection) @connection = connection @admin_user = connection.admin_user @admin_password = connection.admin_password + @port = connection.port end + # Public - Search the domain controller represented by this instance's connection. + # If a referral is returned, search only one of the domain controllers indicated + # by the referral entries, per RFC 4511 (https://tools.ietf.org/html/rfc4511): + # + # "If the client wishes to progress the operation, it contacts one of + # the supported services found in the referral. If multiple URIs are + # present, the client assumes that any supported URI may be used to + # progress the operation." + # + # options - is a hash with the same options that Net::LDAP::Connection#search supports. + # Referral searches will use the given options, but will replace options[:base] + # with the referral URL's base search dn. + # + # Does not take a block argument as GitHub::Ldap and Net::LDAP::Connection#search do. + # + # Will not recursively follow any subsequent referrals. + # + # Returns an Array of Net::LDAP::Entry. def search(options) search_results = [] referral_entries = [] From 644fb68cc74ed05521a3176d2d8fdd462378a60e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:41:30 -0500 Subject: [PATCH 257/307] Updated tests --- test/referral_chaser_test.rb | 109 +++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 823b7d7..8eb1ba4 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -1,47 +1,96 @@ require_relative 'test_helper' +require 'mocha/mini_test' class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test def setup - referral_entries = [ - {:search_referrals => ["ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]} - ] - @chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + @mock_connection = GitHub::Ldap.new({ + admin_user: "Joe", + admin_password: "passworD1", + port: 888 + }) + @chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) end - def test_creates_connection - @chaser.with_referrals do |referral| - assert_equal GitHub::Ldap, referral.connection.class - end + def test_creates_referral_with_connection_credentials + @mock_connection.expects(:search).yields({ search_referrals: ["referral string"]}).returns([]) + + referral = Object.new + referral.stubs(:search).returns([]) + + GitHub::Ldap::ReferralChaser::Referral.expects(:new) + .with("referral string", "Joe", "passworD1", 888) + .returns(referral) + + @chaser.search({}) + end + + def test_creates_referral_with_default_port + mock_connection = GitHub::Ldap.new({ + admin_user: "Joe", + admin_password: "passworD1" + }) + mock_connection.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]}).returns([]) + + stub_conn = Object.new + stub_conn.stubs(:search).returns([]) + GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: 389)).returns(stub_conn) + chaser = GitHub::Ldap::ReferralChaser.new(mock_connection) + chaser.search({}) end - def test_returns_url_escaped_search_base - @chaser.with_referrals do |referral| - assert_equal "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local", referral.search_base - end + def test_creates_referral_for_first_referral_string + @mock_connection.expects(:search).multiple_yields([ + { search_referrals: + ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", + "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] + } + ],[ + { search_referrals: + ["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", + "ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] + } + ]).returns([]) + + referral = Object.new + referral.stubs(:search).returns([]) + + GitHub::Ldap::ReferralChaser::Referral.expects(:new) + .with("ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "Joe", "passworD1", 888) + .returns(referral) + + @chaser.search({}) end - def test_executes_for_every_entry - referral_entries = [ - {:search_referrals => ["test1"]}, - {:search_referrals => ["test2"]} - ] - chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + def test_returns_referral_search_results + @mock_connection.expects(:search).multiple_yields([ + { search_referrals: + ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", + "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] + } + ],[ + { search_referrals: + ["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", + "ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] + } + ]).returns([]) - called = 0 - chaser.with_referrals { called += 1 } - assert_equal 2, called + referral = Object.new + referral.expects(:search).returns(["result", "result"]) + + GitHub::Ldap::ReferralChaser::Referral.expects(:new).returns(referral) + + results = @chaser.search({}) + assert_equal(["result", "result"], results) end - def test_executes_for_every_referral - referral_entries = [ - {:search_referrals => ["test1"]}, - {:search_referrals => ["test2", "test3"]} - ] - chaser = GitHub::Ldap::ReferralChaser.new(referral_entries, "admin1", "passworD1") + def test_referral_should_use_host_from_referral_string + GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local")) + GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "") + end - called = 0 - chaser.with_referrals { called += 1 } - assert_equal 3, called + def test_referral_should_use_host_from_referral_string + GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local")) + GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "") end end From e759522acf0fb7d5a03de2d3c1524cdb870ff6eb Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:51:46 -0500 Subject: [PATCH 258/307] Added a GitHub::Ldap::URL object to encapsulate parsing ldap urls --- lib/github/ldap/url.rb | 83 ++++++++++++++++++++++++++++++++++++++++++ test/url_test.rb | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 lib/github/ldap/url.rb create mode 100644 test/url_test.rb diff --git a/lib/github/ldap/url.rb b/lib/github/ldap/url.rb new file mode 100644 index 0000000..605fd1f --- /dev/null +++ b/lib/github/ldap/url.rb @@ -0,0 +1,83 @@ +module GitHub + class Ldap + + # This class represents an LDAP URL + # + # See: https://tools.ietf.org/html/rfc4516#section-2 + # https://docs.oracle.com/cd/E19957-01/817-6707/urls.html + # + class URL + extend Forwardable + SCOPES = { + "base" => Net::LDAP::SearchScope_BaseObject, + "one" => Net::LDAP::SearchScope_SingleLevel, + "sub" => Net::LDAP::SearchScope_WholeSubtree + } + SCOPES.default = Net::LDAP::SearchScope_BaseObject + + attr_reader :dn, :attributes, :scope, :filter + + def_delegators :@uri, :port, :host, :scheme + + # Public - Creates a new GitHub::Ldap::URL object with :port, :host and :scheme + # delegated to a URI object parsed from url_string, and then parses the + # query params according to the LDAP specification. + # + # url_string - An LDAP URL string. + # returns - a GitHub::Ldap::URL with the following attributes: + # host - Name or IP of the LDAP server. + # port - The given port, defaults to 389. + # dn - The base search DN. + # attributes - The comma-delimited list of attributes to be returned. + # scope - The scope of the search. + # filter - Search filter to apply to entries within the specified scope of the search. + # + # Supported LDAP URL strings look like this, where sections in brackets are optional: + # + # ldap[s]://[hostport][/[dn[?[attributes][?[scope][?[filter]]]]]] + # + # where: + # + # hostport is a host name with an optional ":portnumber" + # dn is the base DN to be used for an LDAP search operation + # attributes is a comma separated list of attributes to be retrieved + # scope is one of these three strings: base one sub (default=base) + # filter is LDAP search filter as used in a call to ldap_search + # + # For example: + # + # ldap://dc4.ghe.local:456/CN=Maggie,DC=dc4,DC=ghe,DC=local?cn,mail?base?(cn=Charlie) + # + def initialize(url_string) + @uri = URI(url_string) + unless ["ldap", "ldaps"].include?(@uri.scheme) + raise InvalidSchemeException.new("Invalid scheme: #{@uri.scheme}") + end + @dn = URI.unescape(@uri.path.sub(/^\//, "")) + if @uri.query + @attributes, @scope, @filter = @uri.query.split("?") + end + end + + # Maps the returned scope value from the URL to one of Net::LDAP::Scopes + # + # The URL scope value can be one of: + # "base" - retrieves information only about the DN (base_dn) specified. + # "one" - retrieves information about entries one level below the DN (base_dn) specified. The base entry is not included in this scope. + # "sub" - retrieves information about entries at all levels below the DN (base_dn) specified. The base entry is included in this scope. + # + # Which will map to one of the following Net::LDAP::Scopes: + # SearchScope_BaseObject = 0 + # SearchScope_SingleLevel = 1 + # SearchScope_WholeSubtree = 2 + # + # If no scope or an invalid scope is given, defaults to SearchScope_BaseObject + def net_ldap_scope + Net::LDAP::SearchScopes[SCOPES[scope]] + end + + class InvalidSchemeException < Exception; end + end + end +end + diff --git a/test/url_test.rb b/test/url_test.rb new file mode 100644 index 0000000..a84127e --- /dev/null +++ b/test/url_test.rb @@ -0,0 +1,78 @@ +require_relative 'test_helper' + +class GitHubLdapURLTestCases < GitHub::Ldap::Test + + def setup + @url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local:123/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local?cn,mail,telephoneNumber?base?(cn=Charlie)") + end + + def test_host + assert_equal "dc4.ghe.local", @url.host + end + + def test_port + assert_equal 123, @url.port + end + + def test_scheme + assert_equal "ldap", @url.scheme + end + + def test_default_port + url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local?attributes?scope?filter") + assert_equal 389, url.port + end + + def test_simple_url + url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local") + assert_equal 389, url.port + assert_equal "dc4.ghe.local", url.host + assert_equal "ldap", url.scheme + assert_equal "", url.dn + assert_equal nil, url.attributes + assert_equal nil, url.filter + assert_equal nil, url.scope + end + + def test_invalid_scheme + ex = assert_raises(GitHub::Ldap::URL::InvalidSchemeException) do + GitHub::Ldap::URL.new("http://dc4.ghe.local") + end + assert_equal("Invalid scheme: http", ex.message) + end + + def test_parse_dn + assert_equal "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local", @url.dn + end + + def test_parse_attributes + assert_equal "cn,mail,telephoneNumber", @url.attributes + end + + def test_parse_filter + assert_equal "(cn=Charlie)", @url.filter + end + + def test_parse_scope + assert_equal "base", @url.scope + end + + def test_default_scope + url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter") + assert_equal "", url.scope + end + + def test_net_ldap_scopes + sub_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?sub?filter") + one_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?one?filter") + base_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?base?filter") + default_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter") + invalid_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe?invalid?filter") + + assert_equal Net::LDAP::SearchScope_BaseObject, base_scope_url.net_ldap_scope + assert_equal Net::LDAP::SearchScope_SingleLevel, one_scope_url.net_ldap_scope + assert_equal Net::LDAP::SearchScope_WholeSubtree, sub_scope_url.net_ldap_scope + assert_equal Net::LDAP::SearchScope_BaseObject, default_scope_url.net_ldap_scope + assert_equal Net::LDAP::SearchScope_BaseObject, invalid_scope_url.net_ldap_scope + end +end From 5cefd9735c709300248e63b79374fa10da6e5af4 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 13:55:00 -0500 Subject: [PATCH 259/307] Remove redundant test --- test/referral_chaser_test.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 8eb1ba4..8169681 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -88,9 +88,4 @@ def test_referral_should_use_host_from_referral_string GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local")) GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "") end - - def test_referral_should_use_host_from_referral_string - GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local")) - GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "") - end end From 0ac330a45911ac161a47b4da40bfd6804e3c2a1a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Mon, 1 Aug 2016 16:36:40 -0500 Subject: [PATCH 260/307] Only iterate over Referall type entries in ReferralChaser --- lib/github/ldap/referral_chaser.rb | 6 ++++-- test/referral_chaser_test.rb | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index 1e0b551..7a36f88 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -44,8 +44,10 @@ def search(options) search_results = [] referral_entries = [] - search_results = connection.search(options) do |referral_entry| - referral_entries << referral_entry + search_results = connection.search(options) do |entry| + if entry[:search_referrals] + referral_entries << entry + end end unless referral_entries.empty? diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 8169681..c9ff2e7 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -84,6 +84,13 @@ def test_returns_referral_search_results assert_equal(["result", "result"], results) end + def test_returns_referral_search_results + @mock_connection.expects(:search).yields({ foo: ["not a referral"] }) + + GitHub::Ldap::ReferralChaser::Referral.expects(:new).never + results = @chaser.search({}) + end + def test_referral_should_use_host_from_referral_string GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local")) GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "") From 1a95540667deab85c51c8000c073d9000c6c0c39 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Tue, 2 Aug 2016 07:44:44 -0500 Subject: [PATCH 261/307] memoize the referral chaser --- lib/github/ldap/membership_validators/active_directory.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 8be26bd..9a79a86 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -36,8 +36,7 @@ def perform(entry) # Chase any potential referrals for an entry that may be owned by a different # domain controller. - chaser = GitHub::Ldap::ReferralChaser.new(ldap) - matched = chaser.search(options) + matched = referral_chaser.search(options) # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive @@ -45,6 +44,10 @@ def perform(entry) result = Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) end + def referral_chaser + @referral_chaser ||= GitHub::Ldap::ReferralChaser.new(@ldap) + end + # Internal: Constructs a membership filter using the "in chain" # extended matching rule afforded by ActiveDirectory. # From e2c36758243bb70904ed8452ffec34fad21d6893 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Tue, 2 Aug 2016 07:45:13 -0500 Subject: [PATCH 262/307] check entry for nil when collecting referrals --- lib/github/ldap/referral_chaser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index 7a36f88..9046cb0 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -45,7 +45,7 @@ def search(options) referral_entries = [] search_results = connection.search(options) do |entry| - if entry[:search_referrals] + if entry && entry[:search_referrals] referral_entries << entry end end From a4573d71891f98f13de89461028e8ad4fbcdff21 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 14:40:26 -0500 Subject: [PATCH 263/307] Remove test file; clean up merge --- ldap.sh | 9 --------- lib/github/ldap.rb | 1 - 2 files changed, 10 deletions(-) delete mode 100644 ldap.sh diff --git a/ldap.sh b/ldap.sh deleted file mode 100644 index 32a654c..0000000 --- a/ldap.sh +++ /dev/null @@ -1,9 +0,0 @@ - ldapsearch -x -h dc4.ghe.local \ - -b "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local" \ - -D "GHE\Administrator" -w "vagrant" \ - "(|(|(| \ - (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-users,CN=Users,DC=ghe,DC=local) \ - (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-users,CN=Users, DC=dc4,DC=ghe,DC=local)) \ - (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=ghe,DC=local)) \ - (memberOf:1.2.840.113556.1.4.1941:=CN=ghe-admins,CN=Users,DC=dc4,DC=ghe,DC=local))" - diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 09df551..fa0c8d6 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -300,7 +300,6 @@ def configure_membership_validation_strategy(strategy = nil) end end -<<<<<<< 96ba488c1e07d7a1f95f1287414805c3b0493b4c # Internal: Set the user search strategy that will be used by # Domain#user?. # From 3eddd8b7929d979c865a214a709734dcaf079f8e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 14:51:02 -0500 Subject: [PATCH 264/307] A bit more merge cleanup --- lib/github/ldap.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index fa0c8d6..f23c2d3 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -12,7 +12,6 @@ require 'github/ldap/membership_validators' require 'github/ldap/user_search/default' require 'github/ldap/user_search/active_directory' -require 'github/ldap/connection_pool' require 'github/ldap/connection_cache' require 'github/ldap/referral_chaser' require 'github/ldap/url' @@ -374,7 +373,5 @@ def configure_member_search_strategy(strategy = nil) def active_directory_capability? capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end - - attr_reader :admin_user, :admin_password end end From 7cdbe8650ff852d82e9a1b12b9efa6ae7416b86a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 14:51:21 -0500 Subject: [PATCH 265/307] Minor style/cleanup --- .../membership_validators/active_directory.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index 9a79a86..e646460 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -24,23 +24,18 @@ def perform(entry) # Sets the entry to the base and scopes the search to the base, # according to the source documentation, found here: # http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx - - filter = membership_in_chain_filter(entry) - options = { - filter: filter, + # + # Use ReferralChaser to chase any potential referrals for an entry that may be owned by a different + # domain controller. + matched = referral_chaser.search \ + filter: membership_in_chain_filter(entry), base: entry.dn, scope: Net::LDAP::SearchScope_BaseObject, return_referrals: true, attributes: ATTRS - } - - # Chase any potential referrals for an entry that may be owned by a different - # domain controller. - matched = referral_chaser.search(options) # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive - result = Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) end From 80a6127bd668783a56f1ddd2f8f6f3da9275485e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 14:54:03 -0500 Subject: [PATCH 266/307] Don't need result reference --- lib/github/ldap/membership_validators/active_directory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/membership_validators/active_directory.rb b/lib/github/ldap/membership_validators/active_directory.rb index e646460..ff4e4fc 100644 --- a/lib/github/ldap/membership_validators/active_directory.rb +++ b/lib/github/ldap/membership_validators/active_directory.rb @@ -36,7 +36,7 @@ def perform(entry) # membership validated if entry was matched and returned as a result # Active Directory DNs are case-insensitive - result = Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) + Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase) end def referral_chaser From f0525598f25775c160507b951c3fd584e80891cd Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 18:17:57 -0500 Subject: [PATCH 267/307] Better format for private method --- lib/github/ldap.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index f23c2d3..0eb2d36 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -362,9 +362,6 @@ def configure_member_search_strategy(strategy = nil) end end - - private - # Internal: Detect whether the LDAP host is an ActiveDirectory server. # # See: http://msdn.microsoft.com/en-us/library/cc223359.aspx. @@ -373,5 +370,6 @@ def configure_member_search_strategy(strategy = nil) def active_directory_capability? capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V51_OID) end + private :active_directory_capability? end end From 77b42095b95629fd9b845c7469732fc8ed935fc9 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 18:27:27 -0500 Subject: [PATCH 268/307] Add host interface to ldap; updated tests --- lib/github/ldap.rb | 1 + lib/github/ldap/user_search/active_directory.rb | 2 +- test/user_search/active_directory_test.rb | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 0eb2d36..6ad1143 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -43,6 +43,7 @@ class Ldap # # Returns the return value of the block. def_delegator :@connection, :open + def_delegator :@connection, :host attr_reader :uid, :search_domains, :virtual_attributes, :membership_validator, diff --git a/lib/github/ldap/user_search/active_directory.rb b/lib/github/ldap/user_search/active_directory.rb index d2e0981..2bec4ad 100644 --- a/lib/github/ldap/user_search/active_directory.rb +++ b/lib/github/ldap/user_search/active_directory.rb @@ -37,7 +37,7 @@ def self.connection(ldap) auth = netldap.instance_variable_get(:@auth) new({ - host: ldap.instance_variable_get(:@host), + host: ldap.host, instrumentation_service: ldap.instrumentation_service, port: encryption ? LDAPS_GC_PORT : STANDARD_GC_PORT, auth: auth, diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index ae23146..1728b46 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -4,7 +4,7 @@ class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test def setup - @ldap = GitHub::Ldap.new(options) + @ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) @ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(@ldap) end @@ -33,14 +33,14 @@ def test_searches_with_empty_base_dn end def test_global_catalog_default_settings - global_catalog = @ad_user_search.global_catalog_connection + global_catalog = GitHub::Ldap::UserSearch::GlobalCatalog.connection(@ldap) instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) auth = global_catalog.instance_variable_get(:@auth) assert_equal :simple, auth[:method] assert_equal "uid=admin,dc=github,dc=com", auth[:username] assert_equal "passworD1", auth[:password] - assert_equal "127.0.0.1", global_catalog.host + assert_equal "ghe.dev", global_catalog.host assert_equal 3268, global_catalog.port assert_equal "MockInstrumentationService", instrumentation_service.class.name end From 518bd851230a5bf143fdb4cfe9e7c9d9a27b1ecf Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 18:30:30 -0500 Subject: [PATCH 269/307] Moved connection cache to its own class --- lib/github/ldap.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 6ad1143..28dfab4 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -323,20 +323,6 @@ def configure_user_search_strategy(strategy) end end - def get_connection_by_host(host, port=389, auth=nil) - auth ||= {:method => :simple, :username => @admin_user, :password => @admin_password} - - @connections ||= Hash.new do |cache, host| - conn = GitHub::Ldap.new({ - host: host, - port: port, - auth: auth - }) - cache[host] = conn - end - @connections[host] - end - # Internal: Configure the member search strategy. # # From 27cf716c6594e241faa919ae495f4342429f343d Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 18:53:04 -0500 Subject: [PATCH 270/307] move mocha requirement to test_helper --- test/domain_test.rb | 1 - test/ldap_test.rb | 1 - test/referral_chaser_test.rb | 1 - test/test_helper.rb | 2 ++ test/user_search/active_directory_test.rb | 1 - test/user_search/default_test.rb | 1 - 6 files changed, 2 insertions(+), 5 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index 324a41a..a7397c8 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -1,5 +1,4 @@ require_relative 'test_helper' -require 'mocha/mini_test' module GitHubLdapDomainTestCases def setup diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 2d47fac..8be1982 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -1,5 +1,4 @@ require_relative 'test_helper' -require 'mocha/mini_test' module GitHubLdapTestCases def setup diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index c9ff2e7..d0ad785 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -1,5 +1,4 @@ require_relative 'test_helper' -require 'mocha/mini_test' class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test diff --git a/test/test_helper.rb b/test/test_helper.rb index 5beca09..856cb5c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,6 +13,8 @@ require 'minitest/mock' require 'minitest/autorun' +require 'mocha/mini_test' + if ENV.fetch('TESTENV', "apacheds") == "apacheds" # Make sure we clean up running test server # NOTE: We need to do this manually since its internal `at_exit` hook diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index 1728b46..2cd4a95 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -1,5 +1,4 @@ require_relative '../test_helper' -require 'mocha/mini_test' class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test diff --git a/test/user_search/default_test.rb b/test/user_search/default_test.rb index 6913d0c..abc230a 100644 --- a/test/user_search/default_test.rb +++ b/test/user_search/default_test.rb @@ -1,5 +1,4 @@ require_relative '../test_helper' -require 'mocha/mini_test' class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test def setup From ef20459ae631f8823ccbeb84fc569e2003da89d0 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 19:00:37 -0500 Subject: [PATCH 271/307] Better use of mocks --- test/domain_test.rb | 4 ++-- test/referral_chaser_test.rb | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index a7397c8..d54850c 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -142,8 +142,8 @@ def test_auth_does_not_bind end def test_user_search_returns_first_entry - entry = Object.new - search_strategy = Object.new + entry = mock("Net::Ldap::Entry") + search_strategy = mock("GitHub::Ldap::UserSearch::Default") search_strategy.stubs(:perform).returns([entry]) @ldap.expects(:user_search_strategy).returns(search_strategy) user = @domain.user?('user1', :attributes => [:cn]) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index d0ad785..f15eaa6 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -14,7 +14,7 @@ def setup def test_creates_referral_with_connection_credentials @mock_connection.expects(:search).yields({ search_referrals: ["referral string"]}).returns([]) - referral = Object.new + referral = mock("GitHub::Ldap::ReferralChaser::Referral") referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) @@ -25,15 +25,12 @@ def test_creates_referral_with_connection_credentials end def test_creates_referral_with_default_port - mock_connection = GitHub::Ldap.new({ - admin_user: "Joe", - admin_password: "passworD1" - }) + mock_connection = mock("GitHub::Ldap") mock_connection.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]}).returns([]) - stub_conn = Object.new - stub_conn.stubs(:search).returns([]) - GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: 389)).returns(stub_conn) + stub_referral_connection = mock("GitHub::Ldap") + stub_referral_connection.stubs(:search).returns([]) + GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: 389)).returns(stub_referral_connection) chaser = GitHub::Ldap::ReferralChaser.new(mock_connection) chaser.search({}) end @@ -51,7 +48,7 @@ def test_creates_referral_for_first_referral_string } ]).returns([]) - referral = Object.new + referral = mock("GitHub::Ldap::ReferralChaser::Referral") referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) @@ -74,7 +71,7 @@ def test_returns_referral_search_results } ]).returns([]) - referral = Object.new + referral = mock("GitHub::Ldap::ReferralChaser::Referral") referral.expects(:search).returns(["result", "result"]) GitHub::Ldap::ReferralChaser::Referral.expects(:new).returns(referral) From 51f793ce771812c0cd39fd4049147e527be5972a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 19:31:39 -0500 Subject: [PATCH 272/307] Fix mock for referral_chaser_test --- test/referral_chaser_test.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index f15eaa6..19c220d 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -25,8 +25,13 @@ def test_creates_referral_with_connection_credentials end def test_creates_referral_with_default_port - mock_connection = mock("GitHub::Ldap") - mock_connection.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]}).returns([]) + mock_connection = GitHub::Ldap.new({ + admin_user: "Joe", + admin_password: "passworD1" + }) + mock_connection.expects(:search).yields({ + search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] + }).returns([]) stub_referral_connection = mock("GitHub::Ldap") stub_referral_connection.stubs(:search).returns([]) From a0f1f24150cb14eaab812be653ffb938bf66d632 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 23:13:28 -0500 Subject: [PATCH 273/307] Fixing CI: don't use a real connection --- test/connection_cache_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/connection_cache_test.rb b/test/connection_cache_test.rb index 7eb0aa3..6adf0bd 100644 --- a/test/connection_cache_test.rb +++ b/test/connection_cache_test.rb @@ -2,19 +2,19 @@ class GitHubLdapConnectionCacheTestCases < GitHub::Ldap::Test - def test_get_connection - conn = GitHub::Ldap::ConnectionCache.get_connection({:host => "host"}) - assert_equal GitHub::Ldap, conn.class + def setup + mock_ldap = mock("GitHub::Ldap") + GitHub::Ldap.stubs(:new).returns(mock_ldap) end def test_returns_cached_connection - conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) - conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) + conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) + conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) assert_equal conn1.object_id, conn2.object_id end def test_creates_new_connections_per_host - conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.local"}) + conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) conn3 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) refute_equal conn1.object_id, conn2.object_id From 88319aa24b6d000c00cf68d49d7d5b0dae0d568c Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 23:28:54 -0500 Subject: [PATCH 274/307] CI Fixes: move all setup code inline --- test/user_search/active_directory_test.rb | 31 ++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index 2cd4a95..2aec199 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -2,37 +2,44 @@ class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test - def setup - @ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) - @ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(@ldap) - end - def test_global_catalog_returns_empty_array_for_no_results + ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) + ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap) + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") mock_global_catalog_connection.expects(:search).returns(nil) - @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) - results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) + ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) + results = ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [], results end def test_global_catalog_returns_array_of_results + ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) + ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap) + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") stub_entry = mock("Net::LDAP::Entry") + mock_global_catalog_connection.expects(:search).returns(stub_entry) - @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) - results = @ad_user_search.perform("login", "CN=Joe", "uid", {}) + ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) + + results = ad_user_search.perform("login", "CN=Joe", "uid", {}) assert_equal [stub_entry], results end def test_searches_with_empty_base_dn + ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) + ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap) + mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") mock_global_catalog_connection.expects(:search).with(has_entry(:base => "")) - @ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) - @ad_user_search.perform("login", "CN=Joe", "uid", {}) + ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) + ad_user_search.perform("login", "CN=Joe", "uid", {}) end def test_global_catalog_default_settings - global_catalog = GitHub::Ldap::UserSearch::GlobalCatalog.connection(@ldap) + ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev')) + global_catalog = GitHub::Ldap::UserSearch::GlobalCatalog.connection(ldap) instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service) auth = global_catalog.instance_variable_get(:@auth) From cc3fd3a36823ed87a9536b22a5000b7d79dbf235 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 23:33:15 -0500 Subject: [PATCH 275/307] Removing ruby 1.9.3 from CI --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 09e4709..7be9a75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby rvm: - - 1.9.3 - 2.1.0 env: From 85ab9f85cf1970e4535c31bc66750a8e5610238a Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 23:36:20 -0500 Subject: [PATCH 276/307] Use correct port for LDAP connection --- test/referral_chaser_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 19c220d..fa512a5 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -6,7 +6,7 @@ def setup @mock_connection = GitHub::Ldap.new({ admin_user: "Joe", admin_password: "passworD1", - port: 888 + port: 389 }) @chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) end @@ -18,7 +18,7 @@ def test_creates_referral_with_connection_credentials referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) - .with("referral string", "Joe", "passworD1", 888) + .with("referral string", "Joe", "passworD1", 389) .returns(referral) @chaser.search({}) From ed66f8da71396f7200959f673d8abadc073bc1d8 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 23:57:36 -0500 Subject: [PATCH 277/307] CI Fixes: use the right credentials to connect to test ldap server --- test/referral_chaser_test.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index fa512a5..f853279 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -4,7 +4,7 @@ class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test def setup @mock_connection = GitHub::Ldap.new({ - admin_user: "Joe", + admin_user: "uid=admin,dc=github,dc=com", admin_password: "passworD1", port: 389 }) @@ -18,7 +18,7 @@ def test_creates_referral_with_connection_credentials referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) - .with("referral string", "Joe", "passworD1", 389) + .with("referral string", "uid=admin,dc=github,dc=com", "passworD1", 389) .returns(referral) @chaser.search({}) @@ -26,7 +26,7 @@ def test_creates_referral_with_connection_credentials def test_creates_referral_with_default_port mock_connection = GitHub::Ldap.new({ - admin_user: "Joe", + admin_user: "uid=admin,dc=github,dc=com", admin_password: "passworD1" }) mock_connection.expects(:search).yields({ @@ -57,7 +57,11 @@ def test_creates_referral_for_first_referral_string referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) - .with("ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "Joe", "passworD1", 888) + .with( + "ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", + "uid=admin,dc=github,dc=com", + "passworD1", + 389) .returns(referral) @chaser.search({}) From 6ad401a460974ce473e671f541b6e0de846511ea Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 5 Aug 2016 00:22:43 -0500 Subject: [PATCH 278/307] CI Fixes: use correct connection attrs for tests --- test/connection_cache_test.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/connection_cache_test.rb b/test/connection_cache_test.rb index 6adf0bd..1d2675c 100644 --- a/test/connection_cache_test.rb +++ b/test/connection_cache_test.rb @@ -2,21 +2,24 @@ class GitHubLdapConnectionCacheTestCases < GitHub::Ldap::Test - def setup - mock_ldap = mock("GitHub::Ldap") - GitHub::Ldap.stubs(:new).returns(mock_ldap) + def options + { + admin_user: "uid=admin,dc=github,dc=com", + admin_password: "passworD1", + port: 389 + } end def test_returns_cached_connection - conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) - conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) + conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) + conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) assert_equal conn1.object_id, conn2.object_id end def test_creates_new_connections_per_host - conn1 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.gh"}) - conn2 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) - conn3 = GitHub::Ldap::ConnectionCache.get_connection({:host => "ghe.dev"}) + conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) + conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.dev")) + conn3 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.dev")) refute_equal conn1.object_id, conn2.object_id assert_equal conn2.object_id, conn3.object_id end From 142e3fd273b2d8ecb59e0127109647aa5b6eab85 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Aug 2016 09:31:31 -0400 Subject: [PATCH 279/307] Test Ruby 2.0 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7be9a75..e46ac80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: ruby rvm: + - 2.0.0 - 2.1.0 env: From 64c6e08eade8c609337aad8f76f3428a699e7576 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Aug 2016 09:41:21 -0400 Subject: [PATCH 280/307] Configure connection with GitHub::Ldap::Test#options Also use that port value, as it changes depending on the integration test LDAP server in use. --- test/referral_chaser_test.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index f853279..95b4da5 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -3,11 +3,7 @@ class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test def setup - @mock_connection = GitHub::Ldap.new({ - admin_user: "uid=admin,dc=github,dc=com", - admin_password: "passworD1", - port: 389 - }) + @mock_connection = GitHub::Ldap.new(options) @chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) end @@ -18,25 +14,21 @@ def test_creates_referral_with_connection_credentials referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) - .with("referral string", "uid=admin,dc=github,dc=com", "passworD1", 389) + .with("referral string", "uid=admin,dc=github,dc=com", "passworD1", options[:port]) .returns(referral) @chaser.search({}) end def test_creates_referral_with_default_port - mock_connection = GitHub::Ldap.new({ - admin_user: "uid=admin,dc=github,dc=com", - admin_password: "passworD1" - }) - mock_connection.expects(:search).yields({ + @mock_connection.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] }).returns([]) stub_referral_connection = mock("GitHub::Ldap") stub_referral_connection.stubs(:search).returns([]) - GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: 389)).returns(stub_referral_connection) - chaser = GitHub::Ldap::ReferralChaser.new(mock_connection) + GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: options[:port])).returns(stub_referral_connection) + chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) chaser.search({}) end @@ -61,7 +53,7 @@ def test_creates_referral_for_first_referral_string "ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "uid=admin,dc=github,dc=com", "passworD1", - 389) + options[:port]) .returns(referral) @chaser.search({}) From 2b8005820670574b9d4d03381d8c808c9fff04c4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Aug 2016 09:42:33 -0400 Subject: [PATCH 281/307] Rename mock_connection to ldap Stylistic preference to be more consistent with other tests. --- test/referral_chaser_test.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 95b4da5..5e51ab5 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -3,12 +3,12 @@ class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test def setup - @mock_connection = GitHub::Ldap.new(options) - @chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) + @ldap = GitHub::Ldap.new(options) + @chaser = GitHub::Ldap::ReferralChaser.new(@ldap) end def test_creates_referral_with_connection_credentials - @mock_connection.expects(:search).yields({ search_referrals: ["referral string"]}).returns([]) + @ldap.expects(:search).yields({ search_referrals: ["referral string"]}).returns([]) referral = mock("GitHub::Ldap::ReferralChaser::Referral") referral.stubs(:search).returns([]) @@ -21,19 +21,19 @@ def test_creates_referral_with_connection_credentials end def test_creates_referral_with_default_port - @mock_connection.expects(:search).yields({ + @ldap.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] }).returns([]) stub_referral_connection = mock("GitHub::Ldap") stub_referral_connection.stubs(:search).returns([]) GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: options[:port])).returns(stub_referral_connection) - chaser = GitHub::Ldap::ReferralChaser.new(@mock_connection) + chaser = GitHub::Ldap::ReferralChaser.new(@ldap) chaser.search({}) end def test_creates_referral_for_first_referral_string - @mock_connection.expects(:search).multiple_yields([ + @ldap.expects(:search).multiple_yields([ { search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] @@ -60,7 +60,7 @@ def test_creates_referral_for_first_referral_string end def test_returns_referral_search_results - @mock_connection.expects(:search).multiple_yields([ + @ldap.expects(:search).multiple_yields([ { search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"] @@ -82,7 +82,7 @@ def test_returns_referral_search_results end def test_returns_referral_search_results - @mock_connection.expects(:search).yields({ foo: ["not a referral"] }) + @ldap.expects(:search).yields({ foo: ["not a referral"] }) GitHub::Ldap::ReferralChaser::Referral.expects(:new).never results = @chaser.search({}) From e7de8e7e45350fb2a784269d81ac905b66319edc Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Fri, 5 Aug 2016 09:53:59 -0400 Subject: [PATCH 282/307] Setup test hosts for connection caching --- .travis.yml | 6 ++++++ test/connection_cache_test.rb | 18 +++++------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index e46ac80..798c5c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,12 @@ env: - TESTENV=openldap - TESTENV=apacheds +# https://docs.travis-ci.com/user/hosts/ +addons: + hosts: + - ad1.ghe.dev + - ad2.ghe.dev + install: - if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi - bundle install diff --git a/test/connection_cache_test.rb b/test/connection_cache_test.rb index 1d2675c..1b55a6b 100644 --- a/test/connection_cache_test.rb +++ b/test/connection_cache_test.rb @@ -2,24 +2,16 @@ class GitHubLdapConnectionCacheTestCases < GitHub::Ldap::Test - def options - { - admin_user: "uid=admin,dc=github,dc=com", - admin_password: "passworD1", - port: 389 - } - end - def test_returns_cached_connection - conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) - conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) + conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev")) + conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev")) assert_equal conn1.object_id, conn2.object_id end def test_creates_new_connections_per_host - conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.gh")) - conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.dev")) - conn3 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ghe.dev")) + conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev")) + conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev")) + conn3 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev")) refute_equal conn1.object_id, conn2.object_id assert_equal conn2.object_id, conn3.object_id end From 02cef0b9859425527c05c2c225957407f2189cbb Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 5 Aug 2016 09:40:37 -0500 Subject: [PATCH 283/307] Document user search strategy callsite --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 28dfab4..2b97111 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -114,7 +114,7 @@ def initialize(options = {}) # configure both the membership validator and the member search strategies configure_search_strategy(options[:search_strategy]) - # configure both the membership validator and the member search strategies + # configure the strategy used by Domain#user? to look up a user entry for login configure_user_search_strategy(options[:user_search_strategy]) # enables instrumenting queries From 15990bef0353a63bca41513c10ec74fb9fc21430 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 5 Aug 2016 10:05:40 -0500 Subject: [PATCH 284/307] Test for invalid URL strings as well as bad schemes in ReferralChaser --- lib/github/ldap/referral_chaser.rb | 8 +++++--- lib/github/ldap/url.rb | 12 ++++++++---- test/referral_chaser_test.rb | 11 +++++++++-- test/url_test.rb | 11 +++++++++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/lib/github/ldap/referral_chaser.rb b/lib/github/ldap/referral_chaser.rb index 9046cb0..4811c51 100644 --- a/lib/github/ldap/referral_chaser.rb +++ b/lib/github/ldap/referral_chaser.rb @@ -53,11 +53,13 @@ def search(options) unless referral_entries.empty? entry = referral_entries.first referral_string = entry[:search_referrals].first - referral = Referral.new(referral_string, admin_user, admin_password, port) - search_results = referral.search(options) + if GitHub::Ldap::URL.valid?(referral_string) + referral = Referral.new(referral_string, admin_user, admin_password, port) + search_results = referral.search(options) + end end - search_results + Array(search_results) end private diff --git a/lib/github/ldap/url.rb b/lib/github/ldap/url.rb index 605fd1f..5c733a7 100644 --- a/lib/github/ldap/url.rb +++ b/lib/github/ldap/url.rb @@ -49,16 +49,20 @@ class URL # ldap://dc4.ghe.local:456/CN=Maggie,DC=dc4,DC=ghe,DC=local?cn,mail?base?(cn=Charlie) # def initialize(url_string) - @uri = URI(url_string) - unless ["ldap", "ldaps"].include?(@uri.scheme) - raise InvalidSchemeException.new("Invalid scheme: #{@uri.scheme}") + if !self.class.valid?(url_string) + raise InvalidLdapURLException.new("Invalid LDAP URL: #{url_string}") end + @uri = URI(url_string) @dn = URI.unescape(@uri.path.sub(/^\//, "")) if @uri.query @attributes, @scope, @filter = @uri.query.split("?") end end + def self.valid?(url_string) + url_string =~ URI::regexp && ["ldap", "ldaps"].include?(URI(url_string).scheme) + end + # Maps the returned scope value from the URL to one of Net::LDAP::Scopes # # The URL scope value can be one of: @@ -76,7 +80,7 @@ def net_ldap_scope Net::LDAP::SearchScopes[SCOPES[scope]] end - class InvalidSchemeException < Exception; end + class InvalidLdapURLException < Exception; end end end end diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index 5e51ab5..a6ca54d 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -8,13 +8,13 @@ def setup end def test_creates_referral_with_connection_credentials - @ldap.expects(:search).yields({ search_referrals: ["referral string"]}).returns([]) + @ldap.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/"]}).returns([]) referral = mock("GitHub::Ldap::ReferralChaser::Referral") referral.stubs(:search).returns([]) GitHub::Ldap::ReferralChaser::Referral.expects(:new) - .with("referral string", "uid=admin,dc=github,dc=com", "passworD1", options[:port]) + .with("ldap://dc1.ghe.local/", "uid=admin,dc=github,dc=com", "passworD1", options[:port]) .returns(referral) @chaser.search({}) @@ -81,6 +81,13 @@ def test_returns_referral_search_results assert_equal(["result", "result"], results) end + def test_handle_blank_url_string_in_referral + @mock_connection.expects(:search).yields({ search_referrals: [""] }) + + results = @chaser.search({}) + assert_equal([], results) + end + def test_returns_referral_search_results @ldap.expects(:search).yields({ foo: ["not a referral"] }) diff --git a/test/url_test.rb b/test/url_test.rb index a84127e..db44ce2 100644 --- a/test/url_test.rb +++ b/test/url_test.rb @@ -35,10 +35,17 @@ def test_simple_url end def test_invalid_scheme - ex = assert_raises(GitHub::Ldap::URL::InvalidSchemeException) do + ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do GitHub::Ldap::URL.new("http://dc4.ghe.local") end - assert_equal("Invalid scheme: http", ex.message) + assert_equal("Invalid LDAP URL: http://dc4.ghe.local", ex.message) + end + + def test_invalid_url + ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do + GitHub::Ldap::URL.new("not a url") + end + assert_equal("Invalid LDAP URL: not a url", ex.message) end def test_parse_dn From 60e7b7fc0b467041f71dfca49a9e6dec02737fa0 Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Fri, 5 Aug 2016 10:17:25 -0500 Subject: [PATCH 285/307] Fixing some merge-fu: use @ldap not @mock_connection --- test/referral_chaser_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb index a6ca54d..3a19973 100644 --- a/test/referral_chaser_test.rb +++ b/test/referral_chaser_test.rb @@ -82,7 +82,7 @@ def test_returns_referral_search_results end def test_handle_blank_url_string_in_referral - @mock_connection.expects(:search).yields({ search_referrals: [""] }) + @ldap.expects(:search).yields({ search_referrals: [""] }) results = @chaser.search({}) assert_equal([], results) From 2b248d342e218fe8005f6b694a807073f2e4c63e Mon Sep 17 00:00:00 2001 From: Dave Sims Date: Thu, 4 Aug 2016 19:05:22 -0500 Subject: [PATCH 286/307] Remove unneeded begin --- lib/github/ldap.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 2b97111..0764d2e 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -311,7 +311,7 @@ def configure_membership_validation_strategy(strategy = nil) # using Active Directory's Global Catalog # functionality. def configure_user_search_strategy(strategy) - @user_search_strategy = begin + @user_search_strategy = case strategy.to_s when "default" GitHub::Ldap::UserSearch::Default.new(self) @@ -320,7 +320,6 @@ def configure_user_search_strategy(strategy) else GitHub::Ldap::UserSearch::Default.new(self) end - end end # Internal: Configure the member search strategy. From 9315cee5510b2d2360d745a404411840b154dda8 Mon Sep 17 00:00:00 2001 From: Colin Seymour Date: Fri, 16 Sep 2016 16:28:02 +0100 Subject: [PATCH 287/307] Use port from options We don't care too much about the port in testing. Using the port in the options allows us to test against ApacheDS and OpenLDAP without adding any additional conditionals. --- test/ldap_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 8105fb8..959c0b3 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -10,17 +10,17 @@ def test_connection_with_default_options end def test_connection_with_list_of_hosts_with_one_valid_host - ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", 3897]])) + ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]]])) assert ldap.test_connection, "Ldap connection expected to succeed" end def test_connection_with_list_of_hosts_with_first_valid - ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", 3897], ["invalid.local", 3897]])) + ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]], ["invalid.local", options[:port]]])) assert ldap.test_connection, "Ldap connection expected to succeed" end def test_connection_with_list_of_hosts_with_first_invalid - ldap = GitHub::Ldap.new(options.merge(hosts: [["invalid.local", 3897], ["localhost", 3897]])) + ldap = GitHub::Ldap.new(options.merge(hosts: [["invalid.local", options[:port]], ["localhost", options[:port]]])) assert ldap.test_connection, "Ldap connection expected to succeed" end From 04f0d1b1a8eeb86f00e4ec225b931ad0e1ebf899 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Fri, 24 Feb 2017 12:14:43 -0800 Subject: [PATCH 288/307] bump net-ldap to 0.16.0 --- CHANGELOG.md | 4 ++++ github-ldap.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae6339..e082762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# v1.10.1 + +* Bump net-ldap to 0.16.0 + # v1.10.0 * Bump net-ldap to 0.15.0 [#92](https://github.com/github/github-ldap/pull/92) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index b8514d0..78938c5 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.15.0' + spec.add_dependency 'net-ldap', '~> 0.16.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' From b7c0b8901104cdd1c77294de4a0e7d69ce2cc189 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Fri, 24 Feb 2017 12:16:05 -0800 Subject: [PATCH 289/307] bump version to 1.10.1 --- github-ldap.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index 78938c5..f39a6dd 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "github-ldap" - spec.version = "1.10.0" + spec.version = "1.10.1" spec.authors = ["David Calavera", "Matt Todd"] spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"] spec.description = %q{LDAP authentication for humans} From cb093d2f1076452bee68752fbc81958489fa8f99 Mon Sep 17 00:00:00 2001 From: Jerry Cheung Date: Fri, 24 Feb 2017 12:16:22 -0800 Subject: [PATCH 290/307] Release 1.10.1 From c8007663132028f0ce41903f47f208807e52f9a4 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Thu, 1 Jun 2017 17:53:09 -0700 Subject: [PATCH 291/307] Enable TLS validation enable by setting `validate_encryption: true` on initialization options --- lib/github/ldap.rb | 11 +++++++---- test/ldap_test.rb | 27 +++++++++++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index b28e440..06f76f0 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -65,6 +65,7 @@ class Ldap # which to attempt opening connections (default [[host, port]]). Overrides # host and port if set. # encryption: optional string. `ssl` or `tls`. nil by default + # validate_encryption: optional boolean. nil by default # admin_user: optional string ldap administrator user dn for authentication # admin_password: optional string ldap administrator user password # @@ -99,7 +100,7 @@ def initialize(options = {}) @connection.authenticate(options[:admin_user], options[:admin_password]) end - if encryption = check_encryption(options[:encryption]) + if encryption = check_encryption(options[:encryption], options[:validate_encryption]) @connection.encryption(encryption) end @@ -236,16 +237,18 @@ def capabilities # Internal - Determine whether to use encryption or not. # # encryption: is the encryption method, either 'ssl', 'tls', 'simple_tls' or 'start_tls'. + # validate: is true to enable certificate validation # # Returns the real encryption type. - def check_encryption(encryption) + def check_encryption(encryption, validate = false) return unless encryption + tls_options = validate == true ? OpenSSL::SSL::VERIFY_PEER : {} case encryption.downcase.to_sym when :ssl, :simple_tls - :simple_tls + { method: :simple_tls, tls_options: tls_options } when :tls, :start_tls - :start_tls + { method: :start_tls, tls_options: tls_options } end end diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 959c0b3..00324e0 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -25,15 +25,30 @@ def test_connection_with_list_of_hosts_with_first_invalid end def test_simple_tls - assert_equal :simple_tls, @ldap.check_encryption(:ssl) - assert_equal :simple_tls, @ldap.check_encryption('SSL') - assert_equal :simple_tls, @ldap.check_encryption(:simple_tls) + expected = { method: :simple_tls, tls_options: {} } + assert_equal expected, @ldap.check_encryption(:ssl) + assert_equal expected, @ldap.check_encryption('SSL') + assert_equal expected, @ldap.check_encryption(:simple_tls) end def test_start_tls - assert_equal :start_tls, @ldap.check_encryption(:tls) - assert_equal :start_tls, @ldap.check_encryption('TLS') - assert_equal :start_tls, @ldap.check_encryption(:start_tls) + expected = { method: :start_tls, tls_options: {} } + assert_equal expected, @ldap.check_encryption(:tls) + assert_equal expected, @ldap.check_encryption('TLS') + assert_equal expected, @ldap.check_encryption(:start_tls) + end + + def test_tls_validation + assert_equal({ method: :start_tls, tls_options: OpenSSL::SSL::VERIFY_PEER }, + @ldap.check_encryption(:tls, true)) + assert_equal({ method: :start_tls, tls_options: {} }, + @ldap.check_encryption(:tls, false)) + assert_equal({ method: :start_tls, tls_options: {} }, + @ldap.check_encryption(:tls, nil)) + assert_equal({ method: :start_tls, tls_options: {} }, + @ldap.check_encryption(:tls, 'true')) + assert_equal({ method: :start_tls, tls_options: {} }, + @ldap.check_encryption(:tls)) end def test_search_delegator From b52284ca3519be60bea4e8e26690b59466fa3d20 Mon Sep 17 00:00:00 2001 From: Sean Bryant Date: Mon, 5 Jun 2017 18:05:22 -0700 Subject: [PATCH 292/307] Use tls_options as a Hash `:verify_mode` is used to specify how the certificate is verified, if at all. --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 06f76f0..1008d94 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -243,7 +243,7 @@ def capabilities def check_encryption(encryption, validate = false) return unless encryption - tls_options = validate == true ? OpenSSL::SSL::VERIFY_PEER : {} + tls_options = { verify_mode: (validate == true ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE } case encryption.downcase.to_sym when :ssl, :simple_tls { method: :simple_tls, tls_options: tls_options } From 07adb9baab59b138f3db5d42eb96ca8cbff4a705 Mon Sep 17 00:00:00 2001 From: Sean Bryant Date: Mon, 5 Jun 2017 18:06:13 -0700 Subject: [PATCH 293/307] Update test to reflect verify_mode --- test/ldap_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 00324e0..4678361 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -39,7 +39,7 @@ def test_start_tls end def test_tls_validation - assert_equal({ method: :start_tls, tls_options: OpenSSL::SSL::VERIFY_PEER }, + assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER } }, @ldap.check_encryption(:tls, true)) assert_equal({ method: :start_tls, tls_options: {} }, @ldap.check_encryption(:tls, false)) From 79065bae794fef53475be01403b4cbf412a01011 Mon Sep 17 00:00:00 2001 From: Sean Bryant Date: Mon, 5 Jun 2017 18:06:44 -0700 Subject: [PATCH 294/307] Fix missing paren --- lib/github/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 1008d94..192d20d 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -243,7 +243,7 @@ def capabilities def check_encryption(encryption, validate = false) return unless encryption - tls_options = { verify_mode: (validate == true ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE } + tls_options = { verify_mode: (validate == true ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE) } case encryption.downcase.to_sym when :ssl, :simple_tls { method: :simple_tls, tls_options: tls_options } From d2be945fe9421ff2452c2764a26ba3519ca77f87 Mon Sep 17 00:00:00 2001 From: Sean Bryant Date: Mon, 5 Jun 2017 18:14:46 -0700 Subject: [PATCH 295/307] Missed a couple of empty tls_options --- test/ldap_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 4678361..2f7a7af 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -25,14 +25,14 @@ def test_connection_with_list_of_hosts_with_first_invalid end def test_simple_tls - expected = { method: :simple_tls, tls_options: {} } + expected = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } assert_equal expected, @ldap.check_encryption(:ssl) assert_equal expected, @ldap.check_encryption('SSL') assert_equal expected, @ldap.check_encryption(:simple_tls) end def test_start_tls - expected = { method: :start_tls, tls_options: {} } + expected = { method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } assert_equal expected, @ldap.check_encryption(:tls) assert_equal expected, @ldap.check_encryption('TLS') assert_equal expected, @ldap.check_encryption(:start_tls) From a7380b20ff9a3a1f04055256376fbc502704084d Mon Sep 17 00:00:00 2001 From: Sean Bryant Date: Mon, 5 Jun 2017 18:24:57 -0700 Subject: [PATCH 296/307] Fix up tests --- test/ldap_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 2f7a7af..5408adc 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -41,13 +41,13 @@ def test_start_tls def test_tls_validation assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER } }, @ldap.check_encryption(:tls, true)) - assert_equal({ method: :start_tls, tls_options: {} }, + assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, @ldap.check_encryption(:tls, false)) - assert_equal({ method: :start_tls, tls_options: {} }, + assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, @ldap.check_encryption(:tls, nil)) - assert_equal({ method: :start_tls, tls_options: {} }, + assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, @ldap.check_encryption(:tls, 'true')) - assert_equal({ method: :start_tls, tls_options: {} }, + assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, @ldap.check_encryption(:tls)) end From 5a50c9cd75a6fbcacaa2115c4203bc9de468aac9 Mon Sep 17 00:00:00 2001 From: Dirkjan Bussink Date: Tue, 6 Jun 2017 10:41:34 +0200 Subject: [PATCH 297/307] Expose TLS options directly --- lib/github/ldap.rb | 7 +++---- test/ldap_test.rb | 16 ++++++---------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 192d20d..2ba8831 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -65,7 +65,7 @@ class Ldap # which to attempt opening connections (default [[host, port]]). Overrides # host and port if set. # encryption: optional string. `ssl` or `tls`. nil by default - # validate_encryption: optional boolean. nil by default + # tls_options: optional hash with TLS options for encrypted connections. Empty by default. # admin_user: optional string ldap administrator user dn for authentication # admin_password: optional string ldap administrator user password # @@ -100,7 +100,7 @@ def initialize(options = {}) @connection.authenticate(options[:admin_user], options[:admin_password]) end - if encryption = check_encryption(options[:encryption], options[:validate_encryption]) + if encryption = check_encryption(options[:encryption], options[:tls_options]) @connection.encryption(encryption) end @@ -240,10 +240,9 @@ def capabilities # validate: is true to enable certificate validation # # Returns the real encryption type. - def check_encryption(encryption, validate = false) + def check_encryption(encryption, tls_options = {}) return unless encryption - tls_options = { verify_mode: (validate == true ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE) } case encryption.downcase.to_sym when :ssl, :simple_tls { method: :simple_tls, tls_options: tls_options } diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 5408adc..2fe9411 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -25,14 +25,14 @@ def test_connection_with_list_of_hosts_with_first_invalid end def test_simple_tls - expected = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } + expected = { method: :simple_tls, tls_options: { } } assert_equal expected, @ldap.check_encryption(:ssl) assert_equal expected, @ldap.check_encryption('SSL') assert_equal expected, @ldap.check_encryption(:simple_tls) end def test_start_tls - expected = { method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } + expected = { method: :start_tls, tls_options: { } } assert_equal expected, @ldap.check_encryption(:tls) assert_equal expected, @ldap.check_encryption('TLS') assert_equal expected, @ldap.check_encryption(:start_tls) @@ -40,15 +40,11 @@ def test_start_tls def test_tls_validation assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER } }, - @ldap.check_encryption(:tls, true)) + @ldap.check_encryption(:tls, verify_mode: OpenSSL::SSL::VERIFY_PEER)) assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, - @ldap.check_encryption(:tls, false)) - assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, - @ldap.check_encryption(:tls, nil)) - assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, - @ldap.check_encryption(:tls, 'true')) - assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }, - @ldap.check_encryption(:tls)) + @ldap.check_encryption(:tls, verify_mode: OpenSSL::SSL::VERIFY_NONE)) + assert_equal({ method: :start_tls, tls_options: { cert_store: "some/path" } }, + @ldap.check_encryption(:tls, cert_store: "some/path")) end def test_search_delegator From d88eb773c46b3d1cb467fed2d40291b4ed71b5b4 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Tue, 6 Jun 2017 11:13:26 -0700 Subject: [PATCH 298/307] update comments --- lib/github/ldap.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 2ba8831..d63282d 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -65,7 +65,9 @@ class Ldap # which to attempt opening connections (default [[host, port]]). Overrides # host and port if set. # encryption: optional string. `ssl` or `tls`. nil by default - # tls_options: optional hash with TLS options for encrypted connections. Empty by default. + # tls_options: optional hash with TLS options for encrypted connections. + # Empty by default. See http://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html + # for available values # admin_user: optional string ldap administrator user dn for authentication # admin_password: optional string ldap administrator user password # @@ -237,7 +239,7 @@ def capabilities # Internal - Determine whether to use encryption or not. # # encryption: is the encryption method, either 'ssl', 'tls', 'simple_tls' or 'start_tls'. - # validate: is true to enable certificate validation + # tls_options: is the options hash for tls encryption method # # Returns the real encryption type. def check_encryption(encryption, tls_options = {}) From 272c61d02f1400b50180173fcf9bd725b5356295 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Tue, 6 Jun 2017 11:13:44 -0700 Subject: [PATCH 299/307] fix tls_options on nil input --- lib/github/ldap.rb | 1 + test/ldap_test.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index d63282d..0bf5be2 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -245,6 +245,7 @@ def capabilities def check_encryption(encryption, tls_options = {}) return unless encryption + tls_options ||= {} case encryption.downcase.to_sym when :ssl, :simple_tls { method: :simple_tls, tls_options: tls_options } diff --git a/test/ldap_test.rb b/test/ldap_test.rb index 2fe9411..d5e9297 100644 --- a/test/ldap_test.rb +++ b/test/ldap_test.rb @@ -45,6 +45,8 @@ def test_tls_validation @ldap.check_encryption(:tls, verify_mode: OpenSSL::SSL::VERIFY_NONE)) assert_equal({ method: :start_tls, tls_options: { cert_store: "some/path" } }, @ldap.check_encryption(:tls, cert_store: "some/path")) + assert_equal({ method: :start_tls, tls_options: {} }, + @ldap.check_encryption(:tls, nil)) end def test_search_delegator From 395488a1a615bdc8928981e4d006766e406e1d11 Mon Sep 17 00:00:00 2001 From: HParker Date: Thu, 13 May 2021 09:37:36 -0700 Subject: [PATCH 300/307] Loosen net-ldap requirement this allows for ldap 0.17.0 This version of net-ldap is required to run ruby 3 without warnings --- github-ldap.gemspec | 2 +- lib/github/ldap.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/github-ldap.gemspec b/github-ldap.gemspec index f39a6dd..a2dad47 100644 --- a/github-ldap.gemspec +++ b/github-ldap.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'net-ldap', '~> 0.16.0' + spec.add_dependency 'net-ldap', '> 0.16.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency 'ladle' diff --git a/lib/github/ldap.rb b/lib/github/ldap.rb index 0bf5be2..33e6627 100644 --- a/lib/github/ldap.rb +++ b/lib/github/ldap.rb @@ -228,7 +228,7 @@ def capabilities instrument "capabilities.github_ldap" do |payload| begin @connection.search_root_dse - rescue Net::LDAP::LdapError => error + rescue Net::LDAP::Error => error payload[:error] = error # stubbed result Net::LDAP::Entry.new From 3f1216f0df1ee1e48dbcb95a930a618457f65104 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:17:06 +0000 Subject: [PATCH 301/307] Fix minitest require --- test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 856cb5c..c29703b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,7 +13,7 @@ require 'minitest/mock' require 'minitest/autorun' -require 'mocha/mini_test' +require 'mocha/minitest' if ENV.fetch('TESTENV', "apacheds") == "apacheds" # Make sure we clean up running test server From b1b0361d7d74acbfee6e1a3c18b94fe276ecbe82 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:25:52 +0000 Subject: [PATCH 302/307] Fix Minitest::Result requirement --- test/member_search/active_directory_test.rb | 6 ++++-- test/membership_validators/active_directory_test.rb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/member_search/active_directory_test.rb b/test/member_search/active_directory_test.rb index e3f367a..19f2c96 100644 --- a/test/member_search/active_directory_test.rb +++ b/test/member_search/active_directory_test.rb @@ -3,7 +3,8 @@ class GitHubLdapActiveDirectoryMemberSearchStubbedTest < GitHub::Ldap::Test # Only run when AD integration tests aren't run def run(*) - self.class.test_env != "activedirectory" ? super : self + return super if self.class.test_env != "activedirectory" + Minitest::Result.from(self) end def find_group(cn) @@ -46,7 +47,8 @@ def test_finds_deeply_nested_group_members class GitHubLdapActiveDirectoryMemberSearchIntegrationTest < GitHub::Ldap::Test # Only run this test suite if ActiveDirectory is configured def run(*) - self.class.test_env == "activedirectory" ? super : self + return super if self.class.test_env == "activedirectory" + Minitest::Result.from(self) end def find_group(cn) diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb index 956fbc5..2160f8d 100644 --- a/test/membership_validators/active_directory_test.rb +++ b/test/membership_validators/active_directory_test.rb @@ -3,7 +3,8 @@ class GitHubLdapActiveDirectoryMembershipValidatorsStubbedTest < GitHub::Ldap::Test # Only run when AD integration tests aren't run def run(*) - self.class.test_env != "activedirectory" ? super : self + return super if self.class.test_env != "activedirectory" + Minitest::Result.from(self) end def setup @@ -72,7 +73,8 @@ def test_does_not_validate_user_not_in_any_group class GitHubLdapActiveDirectoryMembershipValidatorsIntegrationTest < GitHub::Ldap::Test # Only run this test suite if ActiveDirectory is configured def run(*) - self.class.test_env == "activedirectory" ? super : self + return super if self.class.test_env == "activedirectory" + Minitest::Result.from(self) end def setup From 16b8a38e43288e9fb9ddbf642428ac855220a0af Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:26:32 +0000 Subject: [PATCH 303/307] Fix libodbc1 dependency Add source to make slapd dependency available. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 798c5c4..d9fd02b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,10 @@ addons: - ad1.ghe.dev - ad2.ghe.dev +before_install: + - echo "deb http://ftp.br.debian.org/debian stable main" | sudo tee -a /etc/apt/sources.list + - sudo apt-get update + install: - if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi - bundle install From ebfe3c589fe6df5a4a0da70a7babe4d8b5c181c7 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:34:59 +0000 Subject: [PATCH 304/307] Remove DB_CONFIG instructions This config file is not installed anymore. --- script/install-openldap | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/install-openldap b/script/install-openldap index bb0033f..2deddad 100755 --- a/script/install-openldap +++ b/script/install-openldap @@ -13,10 +13,8 @@ TMPDIR=$(mktemp -d) cd $TMPDIR # Delete data and reconfigure. -sudo cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG sudo rm -rf /etc/ldap/slapd.d/* sudo rm -rf /var/lib/ldap/* -sudo cp -v ./DB_CONFIG /var/lib/ldap/DB_CONFIG sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif # Load memberof and ref-int overlays and configure them. sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif From 8cd5fb162295e0b0e11a1eef0fbc8127c14c5562 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:36:22 +0000 Subject: [PATCH 305/307] Return a Result --- test/test_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index c29703b..e92caa6 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -31,8 +31,9 @@ def self.test_env def self.run(reporter, options = {}) start_server - super + result = super stop_server + result end def self.stop_server From 1f623fd6559b2c0ef65a30d211fb76fe06786fcb Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 21:43:00 +0000 Subject: [PATCH 306/307] Fix Minitest::Result requirement --- test/domain_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/domain_test.rb b/test/domain_test.rb index d54850c..4fc0dee 100644 --- a/test/domain_test.rb +++ b/test/domain_test.rb @@ -236,7 +236,8 @@ def test_membership_for_posixGroups class GitHubLdapActiveDirectoryGroupsTest < GitHub::Ldap::Test def run(*) - self.class.test_env == "activedirectory" ? super : self + return super if self.class.test_env == "activedirectory" + Minitest::Result.from(self) end def test_filter_groups From f676fc1d894ef1e1139871e138a3f0c71a8ebcf6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 1 Jun 2021 22:08:01 +0000 Subject: [PATCH 307/307] Fix stub to return an Array Fixes issue with calling to_ary on stubbed result. --- test/user_search/active_directory_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb index 2aec199..32bed79 100644 --- a/test/user_search/active_directory_test.rb +++ b/test/user_search/active_directory_test.rb @@ -20,7 +20,7 @@ def test_global_catalog_returns_array_of_results mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog") stub_entry = mock("Net::LDAP::Entry") - mock_global_catalog_connection.expects(:search).returns(stub_entry) + mock_global_catalog_connection.expects(:search).returns([stub_entry]) ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection) results = ad_user_search.perform("login", "CN=Joe", "uid", {})