github-ldap 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 07498d4477565ea224ca8af669cc442308276298
4
- data.tar.gz: cf73c24745848b687a4efacf7d3eaa19a19418bb
3
+ metadata.gz: 1fe44b5c68117c2b0d616f164f8b40901d2ad2dd
4
+ data.tar.gz: a61c80ebbaabb8ff06793f13910440d403209a7b
5
5
  SHA512:
6
- metadata.gz: 96c0ab7db485b52cd67bc1939eaa68fd85c7ae31515e0b06e2abc780980302ac501b1d070b9c05712e7debf804f2cd0618eb4eca1ff81dbddca3887e2f4f22e2
7
- data.tar.gz: 9ba0caec46275cfd3cced39d07bac7f2ba398a88539f6756d83dea441de784d6d0a198f93a8bc51c9d17c7dc82bbb17fa17ddd588721820e5aa61c3414112f9f
6
+ metadata.gz: 060534d71ad7bfcbd1682699cfa81cebb3552366bd661b94127df79542f97b9c640ebf4414785055ad88923931588bd89ac274a03fc635cb757353c1a717a133
7
+ data.tar.gz: b20400377b5b2d508a910ba6acc3ffeec0a18e5aae7918290c2622be7536a5f625c74a06a31fc2f3d73c0a65cf425ce89e3de4c037a993c2f343c0c1a2bd82d4
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.0.15"
5
+ spec.version = "1.0.16"
6
6
  spec.authors = ["David Calavera"]
7
7
  spec.email = ["david.calavera@gmail.com"]
8
8
  spec.description = %q{Ldap authentication for humans}
data/lib/github/ldap.rb CHANGED
@@ -4,6 +4,7 @@ module GitHub
4
4
  require 'forwardable'
5
5
  require 'github/ldap/filter'
6
6
  require 'github/ldap/domain'
7
+ require 'github/ldap/group'
7
8
 
8
9
  extend Forwardable
9
10
 
@@ -69,5 +70,14 @@ module GitHub
69
70
  def domain(base_name)
70
71
  Domain.new(base_name, @connection, @uid)
71
72
  end
73
+
74
+ # Creates a new group object to perform operations
75
+ #
76
+ # base_name: is the dn of the base root.
77
+ #
78
+ # Returns a new Group object.
79
+ def group(base_name)
80
+ Group.new(self, domain(base_name).bind)
81
+ end
72
82
  end
73
83
  end
@@ -18,6 +18,20 @@ module GitHub
18
18
  @base_name, @connection, @uid = base_name, connection, uid
19
19
  end
20
20
 
21
+ # List all groups under this tree, including subgroups.
22
+ #
23
+ # Returns a list of ldap entries.
24
+ def all_groups
25
+ search(filter: ALL_GROUPS_FILTER)
26
+ end
27
+
28
+ # List all groups under this tree that match the query.
29
+ #
30
+ # Returns a list of ldap entries.
31
+ def filter_groups(query)
32
+ search(filter: group_contains_filter(query))
33
+ end
34
+
21
35
  # List the groups in the ldap server that match the configured ones.
22
36
  #
23
37
  # group_names: is an array of group CNs.
@@ -73,12 +87,7 @@ module GitHub
73
87
  # Returns the user if the login matches any `uid`.
74
88
  # Returns nil if there are no matches.
75
89
  def user?(login)
76
- escaped_login = Net::LDAP::Filter.escape(login)
77
- rs = search(
78
- filter: Net::LDAP::Filter.eq(@uid, escaped_login),
79
- attributes: [],
80
- limit: 1)
81
- rs and rs.first
90
+ search(filter: login_filter(@uid, login), limit: 1).first
82
91
  end
83
92
 
84
93
  # Check if a user can be bound with a password.
@@ -112,13 +121,13 @@ module GitHub
112
121
  # The base option is always overriden.
113
122
  #
114
123
  # Returns an array with the entries found.
115
- # Returns nil if there are no entries.
116
124
  def search(options)
117
125
  options[:base] = @base_name
118
- options[:attributes] ||= %w{ou cn dn sAMAccountName member}
126
+ options[:attributes] ||= []
119
127
  options[:ignore_server_caps] ||= true
128
+ options[:paged_searches_supported] ||= true
120
129
 
121
- @connection.search(options)
130
+ Array(@connection.search(options))
122
131
  end
123
132
 
124
133
  # Provide a meaningful result after a protocol operation (for example,
@@ -130,6 +139,13 @@ module GitHub
130
139
  def get_operation_result
131
140
  @connection.get_operation_result
132
141
  end
142
+
143
+ # Get the entry for this domain.
144
+ #
145
+ # Returns a Net::LDAP::Entry
146
+ def bind
147
+ search({}).first
148
+ end
133
149
  end
134
150
  end
135
151
  end
@@ -1,6 +1,9 @@
1
1
  module GitHub
2
2
  class Ldap
3
3
  module Filter
4
+ ALL_GROUPS_FILTER = Net::LDAP::Filter.eq("objectClass", "groupOfNames") |
5
+ Net::LDAP::Filter.eq("objectClass", "groupOfUniqueNames")
6
+
4
7
  # Filter to get the configured groups in the ldap server.
5
8
  # Takes the list of the group names and generate a filter for the groups
6
9
  # with cn that match and also include members:
@@ -21,11 +24,31 @@ module GitHub
21
24
  # Returns a Net::LDAP::Filter.
22
25
  def member_filter(user_dn = nil)
23
26
  if user_dn
24
- Net::LDAP::Filter.eq("member", user_dn)
27
+ Net::LDAP::Filter.eq("member", user_dn) | Net::LDAP::Filter.eq("uniqueMember", user_dn)
25
28
  else
26
- Net::LDAP::Filter.pres("member")
29
+ Net::LDAP::Filter.pres("member") | Net::LDAP::Filter.pres("uniqueMember")
27
30
  end
28
31
  end
32
+
33
+ # Filter to map a uid with a login.
34
+ # It escapes the login before creating the filter.
35
+ #
36
+ # uid: the entry field to map.
37
+ # login: the login to map.
38
+ #
39
+ # Returns a Net::LDAP::Filter.
40
+ def login_filter(uid, login)
41
+ Net::LDAP::Filter.eq(uid, Net::LDAP::Filter.escape(login))
42
+ end
43
+
44
+ # Filter groups that match a query cn.
45
+ #
46
+ # query: is a string to match the cn with.
47
+ #
48
+ # Returns a Net::LDAP::Filter.
49
+ def group_contains_filter(query)
50
+ Net::LDAP::Filter.contains("cn", query) & ALL_GROUPS_FILTER
51
+ end
29
52
  end
30
53
  end
31
54
  end
@@ -49,7 +49,7 @@ cn: David Calavera
49
49
  cn: David
50
50
  sn: Calavera
51
51
  uid: calavera
52
- userPassword: secret
52
+ userPassword: passworD1
53
53
  mail: calavera@github.com
54
54
  objectClass: inetOrgPerson
55
55
 
@@ -0,0 +1,73 @@
1
+ module GitHub
2
+ class Ldap
3
+ # This class represents an LDAP group.
4
+ # It encapsulates operations that you can perform against a group, like retrieving its members.
5
+ #
6
+ # To get a group, you'll need to create a `Ldap` object and then call the method `group` with the name of its base.
7
+ #
8
+ # For example:
9
+ #
10
+ # domain = GitHub::Ldap.new(options).group("cn=enterprise,dc=github,dc=com")
11
+ #
12
+ class Group
13
+ GROUP_CLASS_NAMES = %w(groupOfNames groupOfUniqueNames)
14
+
15
+ def initialize(ldap, entry)
16
+ @ldap, @entry = ldap, entry
17
+ end
18
+
19
+ # Get all members that belong to a group.
20
+ # This list also includes the members of subgroups.
21
+ #
22
+ # Returns an array with all the member entries.
23
+ def members
24
+ groups, members = member_entries.partition {|e| group?(e[:objectclass])}
25
+ results = members
26
+
27
+ groups.each do |result|
28
+ results.concat @ldap.group(result.dn).members
29
+ end
30
+
31
+ results.uniq {|m| m.dn }
32
+ end
33
+
34
+ # Get all the subgroups from a group recursively.
35
+ #
36
+ # Returns an array with all the subgroup entries.
37
+ def subgroups
38
+ groups, _ = member_entries.partition {|e| group?(e[:objectclass])}
39
+ results = groups
40
+
41
+ groups.each do |result|
42
+ results.concat @ldap.group(result.dn).subgroups
43
+ end
44
+
45
+ results
46
+ end
47
+
48
+ # Get all the member entries for a group.
49
+ #
50
+ # Returns an array of Net::LDAP::Entry.
51
+ def member_entries
52
+ @member_entries ||= member_names.map do |m|
53
+ @ldap.domain(m).bind
54
+ end
55
+ end
56
+
57
+ # Get all the names under `member` and `uniqueMember`.
58
+ #
59
+ # Returns an array with all the DN members.
60
+ def member_names
61
+ @entry[:member] + @entry[:uniqueMember]
62
+ end
63
+
64
+ # Check if an object class includes the member names
65
+ # Use `&` rathen than `include?` because both are arrays.
66
+ #
67
+ # Returns true if the object class includes one of the group class names.
68
+ def group?(object_class)
69
+ !(GROUP_CLASS_NAMES & object_class).empty?
70
+ end
71
+ end
72
+ end
73
+ end
@@ -7,11 +7,11 @@ module GitHub
7
7
 
8
8
  DEFAULT_SERVER_OPTIONS = {
9
9
  user_fixtures: DEFAULT_FIXTURES_PATH,
10
- user_domain: 'dc=github,dc=com',
11
- admin_user: 'uid=admin,dc=github,dc=com',
12
- admin_password: 'secret',
13
- quiet: true,
14
- port: 3897
10
+ user_domain: 'dc=github,dc=com',
11
+ admin_user: 'uid=admin,dc=github,dc=com',
12
+ admin_password: 'secret',
13
+ quiet: true,
14
+ port: 3897
15
15
  }
16
16
 
17
17
  class << self
data/test/domain_test.rb CHANGED
@@ -6,7 +6,7 @@ module GitHubLdapDomainTestCases
6
6
  end
7
7
 
8
8
  def test_user_valid_login
9
- user = @domain.valid_login?('calavera', 'secret')
9
+ user = @domain.valid_login?('calavera', 'passworD1')
10
10
  assert_equal 'uid=calavera,dc=github,dc=com', user.dn
11
11
  end
12
12
 
@@ -25,14 +25,14 @@ module GitHubLdapDomainTestCases
25
25
  end
26
26
 
27
27
  def test_user_in_group
28
- user = @domain.valid_login?('calavera', 'secret')
28
+ user = @domain.valid_login?('calavera', 'passworD1')
29
29
 
30
30
  assert @domain.is_member?(user.dn, %w(Enterprise People)),
31
31
  "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`"
32
32
  end
33
33
 
34
34
  def test_user_not_in_different_group
35
- user = @domain.valid_login?('calavera', 'secret')
35
+ user = @domain.valid_login?('calavera', 'passworD1')
36
36
 
37
37
  assert !@domain.is_member?(user.dn, %w(People)),
38
38
  "Expected `Poeple` not to include the member `#{user.dn}`"
@@ -46,7 +46,7 @@ module GitHubLdapDomainTestCases
46
46
  end
47
47
 
48
48
  def test_authenticate_doesnt_return_invalid_users
49
- user = @domain.authenticate!('calavera', 'secret')
49
+ user = @domain.authenticate!('calavera', 'passworD1')
50
50
  assert_equal 'uid=calavera,dc=github,dc=com', user.dn
51
51
  end
52
52
 
@@ -56,13 +56,13 @@ module GitHubLdapDomainTestCases
56
56
  end
57
57
 
58
58
  def test_authenticate_check_valid_user_and_groups
59
- user = @domain.authenticate!('calavera', 'secret', %w(Enterprise People))
59
+ user = @domain.authenticate!('calavera', 'passworD1', %w(Enterprise People))
60
60
 
61
61
  assert_equal 'uid=calavera,dc=github,dc=com', user.dn
62
62
  end
63
63
 
64
64
  def test_authenticate_doesnt_return_valid_users_in_different_groups
65
- assert !@domain.authenticate!('calavera', 'secret', %w(People)),
65
+ assert !@domain.authenticate!('calavera', 'passworD1', %w(People)),
66
66
  "Expected `authenticate!` to not return an user"
67
67
  end
68
68
 
@@ -109,7 +109,7 @@ module GitHubLdapDomainTestCases
109
109
 
110
110
  def test_auth_binds
111
111
  user = @domain.user?('calavera')
112
- assert @domain.auth(user, 'secret'), 'Expected user to be bound.'
112
+ assert @domain.auth(user, 'passworD1'), 'Expected user to be bound.'
113
113
  end
114
114
 
115
115
  def test_auth_does_not_bind
data/test/filter_test.rb CHANGED
@@ -9,20 +9,20 @@ class FilterTest < Minitest::Test
9
9
  end
10
10
 
11
11
  def test_member_present
12
- assert_equal "(member=*)", @subject.member_filter.to_s
12
+ assert_equal "(|(member=*)(uniqueMember=*))", @subject.member_filter.to_s
13
13
  end
14
14
 
15
15
  def test_member_equal
16
- assert_equal "(member=#{@me})", @subject.member_filter(@me).to_s
16
+ assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))", @subject.member_filter(@me).to_s
17
17
  end
18
18
 
19
19
  def test_groups_reduced
20
- assert_equal "(&(member=*)(|(cn=Enterprise)(cn=People)))",
20
+ assert_equal "(&(|(member=*)(uniqueMember=*))(|(cn=Enterprise)(cn=People)))",
21
21
  @subject.group_filter(%w(Enterprise People)).to_s
22
22
  end
23
23
 
24
24
  def test_groups_for_member
25
- assert_equal "(&(member=#{@me})(|(cn=Enterprise)(cn=People)))",
25
+ assert_equal "(&(|(member=#{@me})(uniqueMember=#{@me}))(|(cn=Enterprise)(cn=People)))",
26
26
  @subject.group_filter(%w(Enterprise People), @me).to_s
27
27
  end
28
28
  end
@@ -0,0 +1,90 @@
1
+ version: 1
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
+ # Admin user
13
+
14
+ dn: uid=admin,dc=github,dc=com
15
+ objectClass: top
16
+ objectClass: person
17
+ objectClass: organizationalPerson
18
+ objectClass: inetOrgPerson
19
+ cn: system administrator
20
+ sn: administrator
21
+ displayName: Directory Superuser
22
+ uid: admin
23
+ userPassword: secret
24
+
25
+ # Groups
26
+
27
+ dn: ou=groups,dc=github,dc=com
28
+ objectclass: organizationalUnit
29
+
30
+ dn: cn=enterprise,ou=groups,dc=github,dc=com
31
+ cn: Enterprise
32
+ objectClass: groupOfNames
33
+ member: uid=calavera,ou=users,dc=github,dc=com
34
+ member: cn=enterprise-devs,ou=groups,dc=github,dc=com
35
+ member: cn=enterprise-ops,ou=groups,dc=github,dc=com
36
+
37
+ dn: cn=enterprise-devs,ou=groups,dc=github,dc=com
38
+ cn: enterprise-devs
39
+ objectClass: groupOfNames
40
+ member: uid=benburkert,ou=users,dc=github,dc=com
41
+
42
+ dn: cn=enterprise-ops,ou=groups,dc=github,dc=com
43
+ cn: enterprise-ops
44
+ objectClass: groupOfNames
45
+ member: uid=sbryant,ou=users,dc=github,dc=com
46
+ member: cn=spaniards,ou=groups,dc=github,dc=com
47
+
48
+ dn: cn=spaniards,ou=groups,dc=github,dc=com
49
+ cn: spaniards
50
+ objectClass: groupOfNames
51
+ member: uid=calavera,ou=users,dc=github,dc=com
52
+ member: uid=rubiojr,ou=users,dc=github,dc=com
53
+
54
+ # Users
55
+
56
+ dn: ou=users,dc=github,dc=com
57
+ objectclass: organizationalUnit
58
+
59
+ dn: uid=calavera,ou=users,dc=github,dc=com
60
+ cn: David Calavera
61
+ cn: David
62
+ sn: Calavera
63
+ uid: calavera
64
+ userPassword: passworD1
65
+ mail: calavera@github.com
66
+ objectClass: inetOrgPerson
67
+
68
+ dn: uid=benburkert,ou=users,dc=github,dc=com
69
+ cn: benburkert
70
+ sn: benburkert
71
+ uid: benburkert
72
+ userPassword: passworD1
73
+ mail: benburkert@github.com
74
+ objectClass: inetOrgPerson
75
+
76
+ dn: uid=sbryant,ou=users,dc=github,dc=com
77
+ cn: sbryant
78
+ sn: sbryant
79
+ uid: sbryant
80
+ userPassword: passworD1
81
+ mail: sbryant@github.com
82
+ objectClass: inetOrgPerson
83
+
84
+ dn: uid=rubiojr,ou=users,dc=github,dc=com
85
+ cn: rubiojr
86
+ sn: rubiojr
87
+ uid: rubiojr
88
+ userPassword: passworD1
89
+ mail: rubiojr@github.com
90
+ objectClass: inetOrgPerson
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ class GitHubLdapGroupTest < GitHub::Ldap::Test
4
+ def self.test_server_options
5
+ {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
6
+ end
7
+
8
+ def groups_domain
9
+ GitHub::Ldap.new(options).domain("ou=groups,dc=github,dc=com")
10
+ end
11
+
12
+ def setup
13
+ @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com")
14
+ end
15
+
16
+ def test_subgroups
17
+ assert_equal 3, @group.subgroups.size
18
+ end
19
+
20
+ def test_members_from_subgroups
21
+ assert_equal 4, @group.members.size
22
+ end
23
+
24
+ def test_all_domain_groups
25
+ groups = groups_domain.all_groups
26
+ assert_equal 4, groups.size
27
+ end
28
+
29
+ def test_filter_domain_groups
30
+ groups = groups_domain.filter_groups('devs')
31
+ assert_equal 1, groups.size
32
+ end
33
+ end
data/test/test_helper.rb CHANGED
@@ -4,6 +4,9 @@ __lib__ = File.expand_path('lib', File.dirname(__FILE__))
4
4
  $LOAD_PATH << __dir__ unless $LOAD_PATH.include?(__dir__)
5
5
  $LOAD_PATH << __lib__ unless $LOAD_PATH.include?(__lib__)
6
6
 
7
+ require 'pathname'
8
+ FIXTURES = Pathname(File.expand_path('fixtures', __dir__))
9
+
7
10
  require 'github/ldap'
8
11
  require 'github/ldap/server'
9
12
 
@@ -21,7 +24,8 @@ class GitHub::Ldap::Test < Minitest::Test
21
24
  end
22
25
 
23
26
  def self.start_server
24
- GitHub::Ldap.start_server
27
+ server_opts = respond_to?(:test_server_options) ? test_server_options : {}
28
+ GitHub::Ldap.start_server(server_opts)
25
29
  end
26
30
 
27
31
  def options
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-ldap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.15
4
+ version: 1.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Calavera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-30 00:00:00.000000000 Z
11
+ date: 2014-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ldap
@@ -98,9 +98,12 @@ files:
98
98
  - lib/github/ldap/domain.rb
99
99
  - lib/github/ldap/filter.rb
100
100
  - lib/github/ldap/fixtures.ldif
101
+ - lib/github/ldap/group.rb
101
102
  - lib/github/ldap/server.rb
102
103
  - test/domain_test.rb
103
104
  - test/filter_test.rb
105
+ - test/fixtures/github-with-subgroups.ldif
106
+ - test/group_test.rb
104
107
  - test/ldap_test.rb
105
108
  - test/test_helper.rb
106
109
  homepage: https://github.com/github/github-ldap
@@ -130,5 +133,8 @@ summary: Ldap client authentication wrapper without all the boilerplate
130
133
  test_files:
131
134
  - test/domain_test.rb
132
135
  - test/filter_test.rb
136
+ - test/fixtures/github-with-subgroups.ldif
137
+ - test/group_test.rb
133
138
  - test/ldap_test.rb
134
139
  - test/test_helper.rb
140
+ has_rdoc: