github-ldap 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: