github-ldap 1.0.15 → 1.0.16

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: 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: