github-ldap 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a23a62005338f49107b2e82997fbdb9c02d4004c
4
- data.tar.gz: bb3a1b3bed1dfb66a1b110bf2e7b31e5a74d7378
3
+ metadata.gz: 8c05560a6e80e800f35e6d9173fa14492b8d7201
4
+ data.tar.gz: 95dd52b0e87152cd37d22b1f804ad336499fd47a
5
5
  SHA512:
6
- metadata.gz: 24548e4cf5b738fb303b635297fd12c634d5a85d42076e367763e833fd0f59e107e398a3f9ff00ce8a69bb4a5d119bf91f5180661d5274e7c6f79e3445ed2bb3
7
- data.tar.gz: 56669b0b27287bdd8cdf3b7820690e2b2832ac9adf12eacb5b882c6d71e687b05c1aa0f4459231738e0001dda538fed02817b13496bbf7e8c4366cc52dacc5c6
6
+ metadata.gz: 6162767914b01776e7d2e8bcab9101f2f7a70acafeee389295abb0caa4953f79e4992181ef01f3465b7f19c576a1781a2b9972de3f815801277937cffa980f96
7
+ data.tar.gz: 381cc12fde5eaba702d4c6abfe7caa07cc75f85f85f644cbd2947fe0bbbb7003c0ca5274ef48a36a28bb89e913ad9271ef78c2276cecf80d1a4fed020fc244e7
data/github-ldap.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "github-ldap"
5
- spec.version = "1.2.1"
5
+ spec.version = "1.3.0"
6
6
  spec.authors = ["David Calavera"]
7
7
  spec.email = ["david.calavera@gmail.com"]
8
8
  spec.description = %q{Ldap authentication for humans}
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
16
  spec.require_paths = ["lib"]
17
17
 
18
- spec.add_dependency 'net-ldap', '>= 0.2.2'
18
+ spec.add_dependency 'net-ldap', '~> 0.7.0'
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency 'ladle'
data/lib/github/ldap.rb CHANGED
@@ -40,11 +40,24 @@ module GitHub
40
40
 
41
41
  configure_virtual_attributes(options[:virtual_attributes])
42
42
 
43
+ # enable fallback recursive group search unless option is false
44
+ @recursive_group_search_fallback = (options[:recursive_group_search_fallback] != false)
45
+
43
46
  # search_domains is a connection of bases to perform searches
44
47
  # when a base is not explicitly provided.
45
48
  @search_domains = Array(options[:search_domains])
46
49
  end
47
50
 
51
+ # Public - Whether membership checks should recurse into nested groups when
52
+ # virtual attributes aren't enabled. The fallback search has poor
53
+ # performance characteristics in some cases, in which case this should be
54
+ # disabled by passing :recursive_group_search_fallback => false.
55
+ #
56
+ # Returns true or false.
57
+ def recursive_group_search_fallback?
58
+ @recursive_group_search_fallback
59
+ end
60
+
48
61
  # Public - Utility method to check if the connection with the server can be stablished.
49
62
  # It tries to bind with the ldap auth default configuration.
50
63
  #
@@ -52,16 +52,22 @@ module GitHub
52
52
  #
53
53
  # Return an Array with the groups that the given user is member of that belong to the given group list.
54
54
  def membership(user_entry, group_names)
55
- all_groups = search(filter: group_filter(group_names))
56
- groups_map = all_groups.each_with_object({}) {|entry, hash| hash[entry.dn] = entry}
57
-
58
- if @ldap.virtual_attributes.enabled?
59
- member_of = groups_map.keys & user_entry[@ldap.virtual_attributes.virtual_membership]
60
- member_of.map {|dn| groups_map[dn]}
61
- else
62
- groups_map.each_with_object([]) do |(dn, group_entry), acc|
63
- acc << group_entry if @ldap.load_group(group_entry).is_member?(user_entry)
55
+ if @ldap.virtual_attributes.enabled? || @ldap.recursive_group_search_fallback?
56
+ all_groups = search(filter: group_filter(group_names))
57
+ groups_map = all_groups.each_with_object({}) {|entry, hash| hash[entry.dn] = entry}
58
+
59
+ if @ldap.virtual_attributes.enabled?
60
+ member_of = groups_map.keys & user_entry[@ldap.virtual_attributes.virtual_membership]
61
+ member_of.map {|dn| groups_map[dn]}
62
+ else # recursive group search fallback
63
+ groups_map.each_with_object([]) do |(dn, group_entry), acc|
64
+ acc << group_entry if @ldap.load_group(group_entry).is_member?(user_entry)
65
+ end
64
66
  end
67
+ else
68
+ # fallback to non-recursive group membership search
69
+ filter = member_filter(user_entry) & group_filter(group_names)
70
+ search(filter: filter)
65
71
  end
66
72
  end
67
73
 
@@ -143,16 +149,6 @@ module GitHub
143
149
  @ldap.search(options, &block)
144
150
  end
145
151
 
146
- # Provide a meaningful result after a protocol operation (for example,
147
- # bind or search) has completed.
148
- #
149
- # Returns an OpenStruct containing an LDAP result code and a
150
- # human-readable string.
151
- # See http://tools.ietf.org/html/rfc4511#appendix-A
152
- def get_operation_result
153
- @ldap.get_operation_result
154
- end
155
-
156
152
  # Get the entry for this domain.
157
153
  #
158
154
  # Returns a Net::LDAP::Entry
@@ -18,16 +18,21 @@ module GitHub
18
18
  group_names.map {|g| Net::LDAP::Filter.eq("cn", g)}.reduce(:|)
19
19
  end
20
20
 
21
- # Filter to check a group membership.
21
+ # Filter to check group membership.
22
22
  #
23
- # user_dn: is an optional user_dn to scope the search to.
23
+ # entry: finds groups this Net::LDAP::Entry is a member of (optional)
24
+ # uid_attr: specifies the memberUid attribute to match with (optional)
24
25
  #
25
26
  # Returns a Net::LDAP::Filter.
26
- def member_filter(user_dn = nil)
27
- if user_dn
28
- MEMBERSHIP_NAMES.map {|n| Net::LDAP::Filter.eq(n, user_dn)}.reduce(:|)
27
+ def member_filter(entry = nil, uid_attr = @ldap.uid)
28
+ if entry
29
+ MEMBERSHIP_NAMES.map {|n| Net::LDAP::Filter.eq(n, entry.dn) }.
30
+ reduce(:|) |
31
+ entry[uid_attr]. map { |uid| Net::LDAP::Filter.eq("memberUid", uid) }.
32
+ reduce(:|)
29
33
  else
30
- MEMBERSHIP_NAMES.map {|n| Net::LDAP::Filter.pres(n)}.reduce(:|)
34
+ (MEMBERSHIP_NAMES + %w(memberUid)).
35
+ map {|n| Net::LDAP::Filter.pres(n)}.reduce(:|)
31
36
  end
32
37
  end
33
38
 
@@ -1,12 +1,5 @@
1
1
  version: 1
2
2
 
3
- # Organizations
4
- dn: dc=github,dc=com
5
- objectClass: dcObject
6
- objectClass: organization
7
- dc: github
8
- o: GitHub Inc.
9
-
10
3
  dn: ou=Group,dc=github,dc=com
11
4
  objectclass: organizationalUnit
12
5
  ou: Group
@@ -1,6 +1,8 @@
1
1
  module GitHub
2
2
  class Ldap
3
3
  class VirtualGroup < Group
4
+ include Filter
5
+
4
6
  def members
5
7
  @ldap.search(filter: members_of_group(@entry.dn, membership_attribute))
6
8
  end
@@ -17,7 +19,7 @@ module GitHub
17
19
  #
18
20
  # Returns a string.
19
21
  def membership_attribute
20
- @ldap.virual_attributes.virtual_membership
22
+ @ldap.virtual_attributes.virtual_membership
21
23
  end
22
24
  end
23
25
  end
data/test/domain_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  module GitHubLdapDomainTestCases
4
4
  def setup
@@ -159,3 +159,63 @@ class GitHubLdapDomainNestedGroupsTest < GitHub::Ldap::Test
159
159
  "Expected `enterprise-ops` to include the member `#{user.dn}`"
160
160
  end
161
161
  end
162
+
163
+ class GitHubLdapPosixGroupsWithRecursionFallbackTest < GitHub::Ldap::Test
164
+ def self.test_server_options
165
+ {
166
+ custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
167
+ user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
168
+ # so we exercise the recursive group search fallback
169
+ recursive_group_search_fallback: true
170
+ }
171
+ end
172
+
173
+ def setup
174
+ @ldap = GitHub::Ldap.new(options)
175
+ @domain = @ldap.domain("dc=github,dc=com")
176
+
177
+ @group = Net::LDAP::Entry._load("""
178
+ dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
179
+ cn: enterprise-posix-devs
180
+ objectClass: posixGroup
181
+ memberUid: benburkert
182
+ memberUid: mtodd""")
183
+ end
184
+
185
+ def test_membership_for_posixGroups
186
+ assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
187
+
188
+ assert @domain.is_member?(user, @group.cn),
189
+ "Expected `#{@group.cn.first}` to include the member `#{user.dn}`"
190
+ end
191
+ end
192
+
193
+ class GitHubLdapPosixGroupsTest < GitHub::Ldap::Test
194
+ def self.test_server_options
195
+ {
196
+ custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
197
+ user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
198
+ # so we test the test the non-recursive group membership search
199
+ recursive_group_search_fallback: false
200
+ }
201
+ end
202
+
203
+ def setup
204
+ @ldap = GitHub::Ldap.new(options)
205
+ @domain = @ldap.domain("dc=github,dc=com")
206
+
207
+ @group = Net::LDAP::Entry._load("""
208
+ dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
209
+ cn: enterprise-posix-devs
210
+ objectClass: posixGroup
211
+ memberUid: benburkert
212
+ memberUid: mtodd""")
213
+ end
214
+
215
+ def test_membership_for_posixGroups
216
+ assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
217
+
218
+ assert @domain.is_member?(user, @group.cn),
219
+ "Expected `#{@group.cn.first}` to include the member `#{user.dn}`"
220
+ end
221
+ end
data/test/filter_test.rb CHANGED
@@ -1,19 +1,35 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  class FilterTest < Minitest::Test
4
- class Subject; include GitHub::Ldap::Filter; end
4
+ class Subject
5
+ include GitHub::Ldap::Filter
6
+ def initialize(ldap)
7
+ @ldap = ldap
8
+ end
9
+ end
10
+
11
+ # Fake a Net::LDAP::Entry
12
+ class Entry < Struct.new(:dn, :uid)
13
+ def [](field)
14
+ Array(send(field))
15
+ end
16
+ end
5
17
 
6
18
  def setup
7
- @subject = Subject.new
8
- @me = 'uid=calavera,dc=github,dc=com'
19
+ @ldap = GitHub::Ldap.new(:uid => 'uid')
20
+ @subject = Subject.new(@ldap)
21
+ @me = 'uid=calavera,dc=github,dc=com'
22
+ @uid = "calavera"
23
+ @entry = Entry.new(@me, @uid)
9
24
  end
10
25
 
11
26
  def test_member_present
12
- assert_equal "(|(member=*)(uniqueMember=*))", @subject.member_filter.to_s
27
+ assert_equal "(|(|(member=*)(uniqueMember=*))(memberUid=*))", @subject.member_filter.to_s
13
28
  end
14
29
 
15
30
  def test_member_equal
16
- assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))", @subject.member_filter(@me).to_s
31
+ assert_equal "(|(|(member=#{@me})(uniqueMember=#{@me}))(memberUid=#{@uid}))",
32
+ @subject.member_filter(@entry).to_s
17
33
  end
18
34
 
19
35
  def test_groups_reduced
@@ -1,14 +1,5 @@
1
1
  version: 1
2
2
 
3
- # Organizations
4
-
5
- dn: dc=github,dc=com
6
- cn: github
7
- objectClass: dcObject
8
- objectClass: organization
9
- dc: github
10
- o: GitHub Inc.
11
-
12
3
  # Admin user
13
4
 
14
5
  dn: uid=admin,dc=github,dc=com
@@ -1,14 +1,5 @@
1
1
  version: 1
2
2
 
3
- # Organizations
4
-
5
- dn: dc=github,dc=com
6
- cn: github
7
- objectClass: dcObject
8
- objectClass: organization
9
- dc: github
10
- o: GitHub Inc.
11
-
12
3
  # Admin user
13
4
 
14
5
  dn: uid=admin,dc=github,dc=com
@@ -0,0 +1,50 @@
1
+ version: 1
2
+
3
+ # Admin user
4
+
5
+ dn: uid=admin,dc=github,dc=com
6
+ objectClass: top
7
+ objectClass: person
8
+ objectClass: organizationalPerson
9
+ objectClass: inetOrgPerson
10
+ cn: system administrator
11
+ sn: administrator
12
+ displayName: Directory Superuser
13
+ uid: admin
14
+ userPassword: secret
15
+
16
+ # Groups
17
+
18
+ dn: ou=groups,dc=github,dc=com
19
+ objectclass: organizationalUnit
20
+ ou: groups
21
+
22
+ # Posix Groups
23
+
24
+ dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
25
+ cn: enterprise-posix-devs
26
+ objectClass: posixGroup
27
+ memberUid: benburkert
28
+ memberUid: mtodd
29
+
30
+ # Users
31
+
32
+ dn: ou=users,dc=github,dc=com
33
+ objectclass: organizationalUnit
34
+ ou: users
35
+
36
+ dn: uid=benburkert,ou=users,dc=github,dc=com
37
+ cn: benburkert
38
+ sn: benburkert
39
+ uid: benburkert
40
+ userPassword: passworD1
41
+ mail: benburkert@github.com
42
+ objectClass: inetOrgPerson
43
+
44
+ dn: uid=mtodd,ou=users,dc=github,dc=com
45
+ cn: mtodd
46
+ sn: mtodd
47
+ uid: mtodd
48
+ userPassword: passworD1
49
+ mail: mtodd@github.com
50
+ objectClass: inetOrgPerson
@@ -1,13 +1,5 @@
1
1
  version: 1
2
2
 
3
- # Organizations
4
-
5
- dn: dc=github,dc=com
6
- objectClass: dcObject
7
- objectClass: organization
8
- dc: github
9
- o: GitHub Inc.
10
-
11
3
  # Admin user
12
4
 
13
5
  dn: uid=admin,dc=github,dc=com
@@ -0,0 +1,26 @@
1
+ version: 1
2
+
3
+ dn: m-oid=1.3.6.1.4.1.18055.0.4.1.2.1001,ou=attributeTypes,cn=other,ou=schema
4
+ objectClass: metaAttributeType
5
+ objectClass: metaTop
6
+ objectClass: top
7
+ m-collective: FALSE
8
+ m-description: memberUid
9
+ m-equality: caseExactMatch
10
+ m-name: memberUid
11
+ m-syntax: 1.3.6.1.4.1.1466.115.121.1.15
12
+ m-usage: USER_APPLICATIONS
13
+ m-oid: 1.3.6.1.4.1.18055.0.4.1.2.1001
14
+
15
+ dn: m-oid=1.3.6.1.4.1.18055.0.4.1.3.1001,ou=objectClasses,cn=other,ou=schema
16
+ objectClass: metaObjectClass
17
+ objectClass: metaTop
18
+ objectClass: top
19
+ m-description: posixGroup
20
+ m-may: cn
21
+ m-may: sn
22
+ m-may: memberUid
23
+ m-supobjectclass: top
24
+ m-name: posixGroup
25
+ m-oid: 1.3.6.1.4.1.18055.0.4.1.3.1001
26
+ m-typeobjectclass: STRUCTURAL
data/test/group_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  class GitHubLdapGroupTest < GitHub::Ldap::Test
4
4
  def self.test_server_options
data/test/ldap_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  module GitHubLdapTestCases
4
4
  def setup
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  class GitHubLdapPosixGroupTest < GitHub::Ldap::Test
4
4
  def self.test_server_options
@@ -48,7 +48,7 @@ objectClass: posixGroup""")
48
48
  members = group.members
49
49
 
50
50
  assert_equal 2, members.size
51
- assert_equal 'benburkert', members.first[:uid].first
51
+ assert_equal %w(benburkert mtodd), members.map(&:uid).flatten.sort
52
52
  end
53
53
 
54
54
  def test_posix_combined_group
metadata CHANGED
@@ -1,83 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-ldap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Calavera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-20 00:00:00.000000000 Z
11
+ date: 2014-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ldap
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.2
19
+ version: 0.7.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.2
26
+ version: 0.7.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ladle
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '5'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '5'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  description: Ldap authentication for humans
@@ -87,8 +87,8 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
- - .gitignore
91
- - .travis.yml
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
92
  - Gemfile
93
93
  - LICENSE.txt
94
94
  - README.md
@@ -107,7 +107,9 @@ files:
107
107
  - test/filter_test.rb
108
108
  - test/fixtures/github-with-looped-subgroups.ldif
109
109
  - test/fixtures/github-with-missing-entries.ldif
110
+ - test/fixtures/github-with-posixGroups.ldif
110
111
  - test/fixtures/github-with-subgroups.ldif
112
+ - test/fixtures/posixGroup.schema.ldif
111
113
  - test/group_test.rb
112
114
  - test/ldap_test.rb
113
115
  - test/posix_group_test.rb
@@ -122,17 +124,17 @@ require_paths:
122
124
  - lib
123
125
  required_ruby_version: !ruby/object:Gem::Requirement
124
126
  requirements:
125
- - - '>='
127
+ - - ">="
126
128
  - !ruby/object:Gem::Version
127
129
  version: '0'
128
130
  required_rubygems_version: !ruby/object:Gem::Requirement
129
131
  requirements:
130
- - - '>='
132
+ - - ">="
131
133
  - !ruby/object:Gem::Version
132
134
  version: '0'
133
135
  requirements: []
134
136
  rubyforge_project:
135
- rubygems_version: 2.0.3
137
+ rubygems_version: 2.2.2
136
138
  signing_key:
137
139
  specification_version: 4
138
140
  summary: Ldap client authentication wrapper without all the boilerplate
@@ -141,9 +143,10 @@ test_files:
141
143
  - test/filter_test.rb
142
144
  - test/fixtures/github-with-looped-subgroups.ldif
143
145
  - test/fixtures/github-with-missing-entries.ldif
146
+ - test/fixtures/github-with-posixGroups.ldif
144
147
  - test/fixtures/github-with-subgroups.ldif
148
+ - test/fixtures/posixGroup.schema.ldif
145
149
  - test/group_test.rb
146
150
  - test/ldap_test.rb
147
151
  - test/posix_group_test.rb
148
152
  - test/test_helper.rb
149
- has_rdoc: