github-ldap 1.0.2 → 1.0.3

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.
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![Build Status](https://travis-ci.org/github/github-ldap.png)
2
+
1
3
  # Github::Ldap
2
4
 
3
5
  GitHub-Ldap is a wrapper on top of Net::LDAP to make it human friendly.
@@ -18,6 +20,8 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
23
+ ### Initialization
24
+
21
25
  GitHub-Ldap let you use an external ldap server to authenticate your users with.
22
26
 
23
27
  There are a few configuration options required to use this adapter:
@@ -27,7 +31,6 @@ There are a few configuration options required to use this adapter:
27
31
  * admin_user: is the the ldap administrator user. Required to perform search operation.
28
32
  * admin_password: is the password for the administrator user. Simple authentication is required on the server.
29
33
  * encryptation: is the encryptation protocol, disabled by default. The valid options are `ssl` and `tls`.
30
- * user_domain: is the default ldap domain base.
31
34
  * uid: is the field name in the ldap server used to authenticate your users, in ActiveDirectory this is `sAMAccountName`.
32
35
 
33
36
  Initialize a new adapter using those required options:
@@ -36,7 +39,28 @@ Initialize a new adapter using those required options:
36
39
  ldap = GitHub::Ldap.new options
37
40
  ```
38
41
 
39
- ## Testing
42
+ ### Quering
43
+
44
+ Searches are performed against an individual domain base, so the first step is to get a new `GitHub::Ldap::Domain` object for the connection:
45
+
46
+ ```ruby
47
+ ldap = GitHub::Ldap.new options
48
+ domain = ldap.domain("dc=github,dc=com")
49
+ ```
50
+
51
+ When we have the domain, we can check if a user can log in with a given password:
52
+
53
+ ```ruby
54
+ domain.valid_login? 'calavera', 'secret'
55
+ ```
56
+
57
+ Or whether a user is member of the given groups:
58
+
59
+ ```ruby
60
+ domain.is_member? 'uid=calavera,dc=github,dc=com', %w(Enterprise)
61
+ ```
62
+
63
+ ### Testing support
40
64
 
41
65
  GitHub-Ldap uses [ladle](https://github.com/NUBIC/ladle) for testing. Ladle is not required by default, so you'll need to add it to your gemfile separatedly and require it.
42
66
 
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.pattern = "test/*_test.rb"
7
+ end
8
+
9
+ task :default => :test
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "github-ldap"
5
- spec.version = "1.0.2"
5
+ spec.version = "1.0.3"
6
6
  spec.authors = ["David Calavera"]
7
7
  spec.email = ["david.calavera@gmail.com"]
8
8
  spec.description = %q{Ldap authentication for humans}
@@ -1,112 +1,23 @@
1
1
  module GitHub
2
2
  class Ldap
3
3
  require 'net/ldap'
4
+ require 'github/ldap/domain'
4
5
 
5
6
  def initialize(options = {})
6
- @user_domain = options[:user_domain]
7
- @uid = options[:uid] || "sAMAccountName"
7
+ @uid = options[:uid] || "sAMAccountName"
8
8
 
9
- @ldap = Net::LDAP.new({
9
+ @connection = Net::LDAP.new({
10
10
  host: options[:host],
11
11
  port: options[:port]
12
12
  })
13
13
 
14
- @ldap.authenticate(options[:admin_user], options[:admin_password])
14
+ @connection.authenticate(options[:admin_user], options[:admin_password])
15
15
 
16
16
  if encryption = check_encryption(options[:encryptation])
17
- @ldap.encryption(encryption)
17
+ @connection.encryption(encryption)
18
18
  end
19
19
  end
20
20
 
21
- # Generate a filter to get the configured groups in the ldap server.
22
- # Takes the list of the group names and generate a filter for the groups
23
- # with cn that match and also include members:
24
- #
25
- # group_names: is an array of group CNs.
26
- #
27
- # Returns the ldap filter.
28
- def group_filter(group_names)
29
- or_filters = group_names.map {|g| Net::LDAP::Filter.eq("cn", g)}.reduce(:|)
30
- Net::LDAP::Filter.pres("member") & or_filters
31
- end
32
-
33
- # List the groups in the ldap server that match the configured ones.
34
- #
35
- # group_names: is an array of group CNs.
36
- #
37
- # Returns a list of ldap entries for the configured groups.
38
- def groups(group_names)
39
- filter = group_filter(group_names)
40
-
41
- @ldap.search(base: @user_domain,
42
- attributes: %w{ou cn dn sAMAccountName member},
43
- filter: filter)
44
- end
45
-
46
- # List the groups that a user is member of.
47
- #
48
- # user_dn: is the dn for the user ldap entry.
49
- # group_names: is an array of group CNs.
50
- #
51
- # Return an Array with the groups that the given user is member of that belong to the given group list.
52
- def membership(user_dn, group_names)
53
- or_filters = group_names.map {|g| Net::LDAP::Filter.eq("cn", g)}.reduce(:|)
54
- member_filter = Net::LDAP::Filter.eq("member", user_dn) & or_filters
55
-
56
- @ldap.search(base: @user_domain,
57
- attributes: %w{ou cn dn sAMAccountName member},
58
- filter: member_filter)
59
- end
60
-
61
-
62
- # Check if the user is include in any of the configured groups.
63
- #
64
- # user_dn: is the dn for the user ldap entry.
65
- # group_names: is an array of group CNs.
66
- #
67
- # Returns true if the user belongs to any of the groups.
68
- # Returns false otherwise.
69
- def is_member?(user_dn, group_names)
70
- return true if group_names.nil?
71
- return true if group_names.empty?
72
-
73
- user_membership = membership(user_dn, group_names)
74
-
75
- !user_membership.empty?
76
- end
77
-
78
- # Check if the user credentials are valid.
79
- #
80
- # login: is the user's login.
81
- # password: is the user's password.
82
- #
83
- # Returns a Ldap::Entry if the credentials are valid.
84
- # Returns nil if the credentials are invalid.
85
- def valid_login?(login, password)
86
- result = @ldap.bind_as(
87
- base: @user_domain,
88
- limit: 1,
89
- filter: Net::LDAP::Filter.eq(@uid, login),
90
- password: password)
91
-
92
- return result.first if result.is_a?(Array)
93
- end
94
-
95
- # Authenticate a user with the ldap server.
96
- #
97
- # login: is the user's login. This method doesn't accept email identifications.
98
- # password: is the user's password.
99
- # group_names: is an array of group CNs.
100
- #
101
- # Returns the user info if the credentials are valid and there are no groups configured.
102
- # Returns the user info if the credentials are valid and the user belongs to a configured group.
103
- # Returns nil if the credentials are invalid
104
- def authenticate!(login, password, group_names = nil)
105
- user = valid_login?(login, password)
106
-
107
- return user if user && is_member?(user.dn, group_names)
108
- end
109
-
110
21
  # Check the legacy auth configuration options (before David's war with omniauth)
111
22
  # to determine whether to use encryptation or not.
112
23
  #
@@ -131,7 +42,16 @@ module GitHub
131
42
  # Return false if the authentication settings are not valid.
132
43
  # Raises an Net::LDAP::LdapError if the connection fails.
133
44
  def test_connection
134
- @ldap.bind
45
+ @connection.bind
46
+ end
47
+
48
+ # Creates a new domain object to perform operations
49
+ #
50
+ # base_name: is the dn of the base root.
51
+ #
52
+ # Returns a new Domain object.
53
+ def domain(base_name)
54
+ Domain.new(base_name, @connection, @uid)
135
55
  end
136
56
  end
137
57
  end
@@ -0,0 +1,108 @@
1
+ module GitHub
2
+ class Ldap
3
+ # A domain represents the base object for an ldap tree.
4
+ # It encapsulates the operations that you can perform against a tree, authenticating users, for instance.
5
+ #
6
+ # This makes possible to reuse a server connection to perform operations with two different domain bases.
7
+ #
8
+ # To get a domain, you'll need to create a `Ldap` object and then call the method `domain` with the name of the base.
9
+ #
10
+ # For example:
11
+ #
12
+ # domain = GitHub::Ldap.new(options).domain("dc=github,dc=com")
13
+ #
14
+ class Domain
15
+ def initialize(base_name, connection, uid)
16
+ @base_name, @connection, @uid = base_name, connection, uid
17
+ end
18
+
19
+ # Generate a filter to get the configured groups in the ldap server.
20
+ # Takes the list of the group names and generate a filter for the groups
21
+ # with cn that match and also include members:
22
+ #
23
+ # group_names: is an array of group CNs.
24
+ #
25
+ # Returns the ldap filter.
26
+ def group_filter(group_names)
27
+ or_filters = group_names.map {|g| Net::LDAP::Filter.eq("cn", g)}.reduce(:|)
28
+ Net::LDAP::Filter.pres("member") & or_filters
29
+ end
30
+
31
+ # List the groups in the ldap server that match the configured ones.
32
+ #
33
+ # group_names: is an array of group CNs.
34
+ #
35
+ # Returns a list of ldap entries for the configured groups.
36
+ def groups(group_names)
37
+ filter = group_filter(group_names)
38
+
39
+ @connection.search(base: @base_name,
40
+ attributes: %w{ou cn dn sAMAccountName member},
41
+ filter: filter)
42
+ end
43
+
44
+ # List the groups that a user is member of.
45
+ #
46
+ # user_dn: is the dn for the user ldap entry.
47
+ # group_names: is an array of group CNs.
48
+ #
49
+ # Return an Array with the groups that the given user is member of that belong to the given group list.
50
+ def membership(user_dn, group_names)
51
+ or_filters = group_names.map {|g| Net::LDAP::Filter.eq("cn", g)}.reduce(:|)
52
+ member_filter = Net::LDAP::Filter.eq("member", user_dn) & or_filters
53
+
54
+ @connection.search(base: @base_name,
55
+ attributes: %w{ou cn dn sAMAccountName member},
56
+ filter: member_filter)
57
+ end
58
+
59
+ # Check if the user is include in any of the configured groups.
60
+ #
61
+ # user_dn: is the dn for the user ldap entry.
62
+ # group_names: is an array of group CNs.
63
+ #
64
+ # Returns true if the user belongs to any of the groups.
65
+ # Returns false otherwise.
66
+ def is_member?(user_dn, group_names)
67
+ return true if group_names.nil?
68
+ return true if group_names.empty?
69
+
70
+ user_membership = membership(user_dn, group_names)
71
+
72
+ !user_membership.empty?
73
+ end
74
+
75
+ # Check if the user credentials are valid.
76
+ #
77
+ # login: is the user's login.
78
+ # password: is the user's password.
79
+ #
80
+ # Returns a Ldap::Entry if the credentials are valid.
81
+ # Returns nil if the credentials are invalid.
82
+ def valid_login?(login, password)
83
+ result = @connection.bind_as(
84
+ base: @base_name,
85
+ limit: 1,
86
+ filter: Net::LDAP::Filter.eq(@uid, login),
87
+ password: password)
88
+
89
+ return result.first if result.is_a?(Array)
90
+ end
91
+
92
+ # Authenticate a user with the ldap server.
93
+ #
94
+ # login: is the user's login. This method doesn't accept email identifications.
95
+ # password: is the user's password.
96
+ # group_names: is an array of group CNs.
97
+ #
98
+ # Returns the user info if the credentials are valid and there are no groups configured.
99
+ # Returns the user info if the credentials are valid and the user belongs to a configured group.
100
+ # Returns nil if the credentials are invalid
101
+ def authenticate!(login, password, group_names = nil)
102
+ user = valid_login?(login, password)
103
+
104
+ return user if user && is_member?(user.dn, group_names)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -38,7 +38,8 @@ module GitHub
38
38
  ldif: server_options[:user_fixtures],
39
39
  domain: server_options[:user_domain],
40
40
  port: server_options[:port],
41
- quiet: server_options[:quiet])
41
+ quiet: server_options[:quiet],
42
+ tmpdir: server_tmp)
42
43
 
43
44
  @ldap_server.start
44
45
  end
@@ -48,5 +49,20 @@ module GitHub
48
49
  def self.stop_server
49
50
  ldap_server && ldap_server.stop
50
51
  end
52
+
53
+ # Determine the temporal directory where the ldap server lives.
54
+ # If there is no temporal directory in the environment we create one in the base path.
55
+ #
56
+ # Returns the path to the temporal directory.
57
+ def self.server_tmp
58
+ tmp = ENV['TMPDIR'] || ENV['TEMPDIR']
59
+
60
+ if tmp.nil?
61
+ tmp = 'tmp'
62
+ Dir.mkdir(tmp)
63
+ end
64
+
65
+ tmp
66
+ end
51
67
  end
52
68
  end
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+
3
+ class GitHubLdapDomainTest < Minitest::Test
4
+ def setup
5
+ GitHub::Ldap.start_server
6
+
7
+ @options = GitHub::Ldap.server_options.merge \
8
+ host: 'localhost',
9
+ uid: 'uid'
10
+
11
+ @domain = GitHub::Ldap.new(@options).domain("dc=github,dc=com")
12
+ end
13
+
14
+ def teardown
15
+ GitHub::Ldap.stop_server
16
+ end
17
+
18
+ def test_user_valid_login
19
+ user = @domain.valid_login?('calavera', 'secret')
20
+ assert_equal 'uid=calavera,dc=github,dc=com', user.dn
21
+ end
22
+
23
+ def test_user_with_invalid_password
24
+ assert !@domain.valid_login?('calavera', 'foo'),
25
+ "Login `calavera` expected to be invalid with password `foo`"
26
+ end
27
+
28
+ def test_user_with_invalid_login
29
+ assert !@domain.valid_login?('bar', 'foo'),
30
+ "Login `bar` expected to be invalid with password `foo`"
31
+ end
32
+
33
+ def test_groups_in_server
34
+ assert_equal 2, @domain.groups(%w(Enterprise People)).size
35
+ end
36
+
37
+ def test_user_in_group
38
+ user = @domain.valid_login?('calavera', 'secret')
39
+
40
+ assert @domain.is_member?(user.dn, %w(Enterprise People)),
41
+ "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`"
42
+ end
43
+
44
+ def test_user_not_in_different_group
45
+ user = @domain.valid_login?('calavera', 'secret')
46
+
47
+ assert !@domain.is_member?(user.dn, %w(People)),
48
+ "Expected `Poeple` not to include the member `#{user.dn}`"
49
+ end
50
+
51
+ def test_user_without_group
52
+ user = @domain.valid_login?('ldaptest', 'secret')
53
+
54
+ assert !@domain.is_member?(user.dn, %w(People)),
55
+ "Expected `Poeple` not to include the member `#{user.dn}`"
56
+ end
57
+
58
+ def test_authenticate_doesnt_return_invalid_users
59
+ user = @domain.authenticate!('calavera', 'secret')
60
+ assert_equal 'uid=calavera,dc=github,dc=com', user.dn
61
+ end
62
+
63
+ def test_authenticate_doesnt_return_invalid_users
64
+ assert !@domain.authenticate!('calavera', 'foo'),
65
+ "Expected `authenticate!` to not return an invalid user"
66
+ end
67
+
68
+ def test_authenticate_check_valid_user_and_groups
69
+ user = @domain.authenticate!('calavera', 'secret', %w(Enterprise People))
70
+
71
+ assert_equal 'uid=calavera,dc=github,dc=com', user.dn
72
+ end
73
+
74
+ def test_authenticate_doesnt_return_valid_users_in_different_groups
75
+ assert !@domain.authenticate!('calavera', 'secret', %w(People)),
76
+ "Expected `authenticate!` to not return an user"
77
+ end
78
+
79
+ def test_membership_empty_for_non_members
80
+ assert @domain.membership('uid=calavera,dc=github,dc=com', %w(People)).empty?,
81
+ "Expected `calavera` not to be a member of `People`."
82
+ end
83
+
84
+ def test_membership_groups_for_members
85
+ groups = @domain.membership('uid=calavera,dc=github,dc=com', %w(Enterprise People))
86
+
87
+ assert_equal 1, groups.size
88
+ assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
89
+ end
90
+ end
91
+
@@ -19,67 +19,6 @@ class GitHubLdapTest < Minitest::Test
19
19
  assert @ldap.test_connection, "Ldap connection expected to succeed"
20
20
  end
21
21
 
22
- def test_user_valid_login
23
- user = @ldap.valid_login?('calavera', 'secret')
24
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
25
- end
26
-
27
- def test_user_with_invalid_password
28
- assert !@ldap.valid_login?('calavera', 'foo'),
29
- "Login `calavera` expected to be invalid with password `foo`"
30
- end
31
-
32
- def test_user_with_invalid_login
33
- assert !@ldap.valid_login?('bar', 'foo'),
34
- "Login `bar` expected to be invalid with password `foo`"
35
- end
36
-
37
- def test_groups_in_server
38
- assert_equal 2, @ldap.groups(%w(Enterprise People)).size
39
- end
40
-
41
- def test_user_in_group
42
- user = @ldap.valid_login?('calavera', 'secret')
43
-
44
- assert @ldap.is_member?(user.dn, %w(Enterprise People)),
45
- "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`"
46
- end
47
-
48
- def test_user_not_in_different_group
49
- user = @ldap.valid_login?('calavera', 'secret')
50
-
51
- assert !@ldap.is_member?(user.dn, %w(People)),
52
- "Expected `Poeple` not to include the member `#{user.dn}`"
53
- end
54
-
55
- def test_user_without_group
56
- user = @ldap.valid_login?('ldaptest', 'secret')
57
-
58
- assert !@ldap.is_member?(user.dn, %w(People)),
59
- "Expected `Poeple` not to include the member `#{user.dn}`"
60
- end
61
-
62
- def test_authenticate_doesnt_return_invalid_users
63
- user = @ldap.authenticate!('calavera', 'secret')
64
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
65
- end
66
-
67
- def test_authenticate_doesnt_return_invalid_users
68
- assert !@ldap.authenticate!('calavera', 'foo'),
69
- "Expected `authenticate!` to not return an invalid user"
70
- end
71
-
72
- def test_authenticate_check_valid_user_and_groups
73
- user = @ldap.authenticate!('calavera', 'secret', %w(Enterprise People))
74
-
75
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
76
- end
77
-
78
- def test_authenticate_doesnt_return_valid_users_in_different_groups
79
- assert !@ldap.authenticate!('calavera', 'secret', %w(People)),
80
- "Expected `authenticate!` to not return an user"
81
- end
82
-
83
22
  def test_simple_tls
84
23
  assert_equal :simple_tls, @ldap.check_encryption(:ssl)
85
24
  assert_equal :simple_tls, @ldap.check_encryption(:simple_tls)
@@ -89,16 +28,4 @@ class GitHubLdapTest < Minitest::Test
89
28
  assert_equal :start_tls, @ldap.check_encryption(:tls)
90
29
  assert_equal :start_tls, @ldap.check_encryption(:start_tls)
91
30
  end
92
-
93
- def test_membership_empty_for_non_members
94
- assert @ldap.membership('uid=calavera,dc=github,dc=com', %w(People)).empty?,
95
- "Expected `calavera` not to be a member of `People`."
96
- end
97
-
98
- def test_membership_groups_for_members
99
- groups = @ldap.membership('uid=calavera,dc=github,dc=com', %w(Enterprise People))
100
-
101
- assert_equal 1, groups.size
102
- assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
103
- end
104
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-ldap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-08 00:00:00.000000000 Z
12
+ date: 2013-07-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-ldap
@@ -99,14 +99,17 @@ extensions: []
99
99
  extra_rdoc_files: []
100
100
  files:
101
101
  - .gitignore
102
+ - .travis.yml
102
103
  - Gemfile
103
104
  - LICENSE.txt
104
105
  - README.md
105
106
  - Rakefile
106
107
  - github-ldap.gemspec
107
108
  - lib/github/ldap.rb
109
+ - lib/github/ldap/domain.rb
108
110
  - lib/github/ldap/fixtures.ldif
109
111
  - lib/github/ldap/server.rb
112
+ - test/domain_test.rb
110
113
  - test/ldap_test.rb
111
114
  - test/test_helper.rb
112
115
  homepage: https://github.com/github/github-ldap
@@ -135,6 +138,7 @@ signing_key:
135
138
  specification_version: 3
136
139
  summary: Ldap client authentication wrapper without all the boilerplate
137
140
  test_files:
141
+ - test/domain_test.rb
138
142
  - test/ldap_test.rb
139
143
  - test/test_helper.rb
140
144
  has_rdoc: