devise_ldap_authenticatable 0.8.5 → 0.8.6
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 +4 -4
- data/.gitignore +2 -1
- data/README.md +9 -10
- data/devise_ldap_authenticatable.gemspec +9 -9
- data/lib/devise_ldap_authenticatable.rb +17 -7
- data/lib/devise_ldap_authenticatable/ldap/adapter.rb +17 -10
- data/lib/devise_ldap_authenticatable/ldap/connection.rb +64 -11
- data/lib/devise_ldap_authenticatable/model.rb +1 -1
- data/lib/devise_ldap_authenticatable/strategy.rb +1 -1
- data/lib/devise_ldap_authenticatable/version.rb +1 -1
- data/lib/generators/devise_ldap_authenticatable/install_generator.rb +5 -4
- data/lib/generators/devise_ldap_authenticatable/templates/ldap.yml +7 -1
- data/spec/rails_app/config/ldap.yml +4 -2
- data/spec/rails_app/config/ldap_with_erb.yml +5 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/adapter_spec.rb +21 -0
- data/spec/unit/connection_spec.rb +89 -0
- data/spec/unit/user_spec.rb +45 -12
- metadata +7 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 32d0f8a193c1b3669af1a6f2271f62a750bc2d49
|
|
4
|
+
data.tar.gz: afefa657196cbbccd64b415f0549d40779647427
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '029eeaad644b3d7026039cc535f15e0e280ad1af4a9e7af803e638c7f6861b8abd5fdf81c0ada09d4164a82fe00ab2569594b9fb06c04f18e904eadd67eb970c'
|
|
7
|
+
data.tar.gz: 430fd5c92a054178f1a209fba7673fb8e578b70c718b4aaf047131fdc9f591979db4bdf5d30dd2e0d2ea63b37cc231bf53d738ed363963af3353e3382000b91a
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
Devise LDAP Authenticatable
|
|
2
2
|
===========================
|
|
3
|
-
|
|
4
|
-
Why this fork?
|
|
5
|
-
--------------
|
|
6
|
-
This fork changes a few lines to allow the admin binding to be set to the user trying to log in.
|
|
7
|
-
|
|
8
3
|
[](http://badge.fury.io/rb/devise_ldap_authenticatable)
|
|
9
4
|
[](https://codeclimate.com/github/cschiewek/devise_ldap_authenticatable)
|
|
10
5
|
[](https://gemnasium.com/cschiewek/devise_ldap_authenticatable)
|
|
@@ -15,7 +10,7 @@ If you are building applications for use within your organization which require
|
|
|
15
10
|
|
|
16
11
|
Devise LDAP Authenticatable works in replacement of Database Authenticatable. This devise plugin has not been tested with DatabaseAuthenticatable enabled at the same time. This is meant as a drop in replacement for DatabaseAuthenticatable allowing for a semi single sign on approach.
|
|
17
12
|
|
|
18
|
-
For a screencast with an example application, please visit: [http://
|
|
13
|
+
For a screencast with an example application, please visit: [http://corrupt.net/2010/07/05/LDAP-Authentication-With-Devise/](http://corrupt.net/2010/07/05/LDAP-Authentication-With-Devise/)
|
|
19
14
|
|
|
20
15
|
Prerequisites
|
|
21
16
|
-------------
|
|
@@ -24,6 +19,8 @@ Prerequisites
|
|
|
24
19
|
|
|
25
20
|
Note: Rails 3.x / Devise 2.x has been moved to the 0.7 branch. All 0.7.x gems will support Rails 3, where as 0.8.x will support Rails 4.
|
|
26
21
|
|
|
22
|
+
If you are transitioning from having Devise manage your users' passwords in the database to using LDAP auth, you may have to update your `users` table to make `encrypted_password` nullable, or else the LDAP user insert will fail.
|
|
23
|
+
|
|
27
24
|
Usage
|
|
28
25
|
-----
|
|
29
26
|
In the Gemfile for your application:
|
|
@@ -83,7 +80,9 @@ In initializer `config/initializers/devise.rb` :
|
|
|
83
80
|
* `ldap_check_group_membership` _(default: false)_
|
|
84
81
|
* When set to true, the user trying to login will be checked to make sure they are in all of groups specified in the ldap.yml file.
|
|
85
82
|
* `ldap_check_attributes` _(default: false)_
|
|
86
|
-
* When set to true, the user trying to login will be checked to make sure
|
|
83
|
+
* When set to true, the user trying to login will be checked to make sure their attributes match those specified in the ldap.yml file.
|
|
84
|
+
* `ldap_check_attributes_presence` _(default: false)_
|
|
85
|
+
* When set to true, the user trying to login will be checked against all `require_attribute_presence` attributes in the ldap.yml file, either present _(attr: true)_,or not present _(attr: false)_.
|
|
87
86
|
* `ldap_use_admin_to_bind` _(default: false)_
|
|
88
87
|
* When set to true, the admin user will be used to bind to the LDAP server during authentication.
|
|
89
88
|
* `ldap_check_group_membership_without_admin` _(default: false)_
|
|
@@ -116,9 +115,9 @@ Devise LDAP Authenticatable uses a running OpenLDAP server to do automated accep
|
|
|
116
115
|
|
|
117
116
|
On OS X, this is available out of the box.
|
|
118
117
|
|
|
119
|
-
On Ubuntu, you can install OpenLDAP with `sudo apt-get install slapd ldap-utils`. If slapd runs under AppArmor, add an exception like this to `/etc/apparmor.d/local/usr.sbin.slapd` to let slapd read our configs.
|
|
118
|
+
On Ubuntu, you can install OpenLDAP with `sudo apt-get install slapd ldap-utils`. If slapd runs under AppArmor, add an exception like this to `/etc/apparmor.d/local/usr.sbin.slapd` to let slapd read our configs (reload using `sudo service apparmor reload` afterwards).
|
|
120
119
|
|
|
121
|
-
/path/to/devise_ldap_authenticatable/spec/ldap/** rw
|
|
120
|
+
/path/to/devise_ldap_authenticatable/spec/ldap/** rw,
|
|
122
121
|
|
|
123
122
|
To start hacking on `devise_ldap_authentication`, clone the github repository, start the test LDAP server, and run the rake test task:
|
|
124
123
|
|
|
@@ -129,7 +128,7 @@ To start hacking on `devise_ldap_authentication`, clone the github repository, s
|
|
|
129
128
|
# in a separate console or backgrounded
|
|
130
129
|
./spec/ldap/run-server
|
|
131
130
|
|
|
132
|
-
bundle exec rake db:migrate # first time only
|
|
131
|
+
RAILS_ENV=test bundle exec rake db:migrate # first time only
|
|
133
132
|
bundle exec rake spec
|
|
134
133
|
|
|
135
134
|
References
|
|
@@ -18,16 +18,16 @@ Gem::Specification.new do |s|
|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
19
|
s.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
s.add_dependency
|
|
22
|
-
s.add_dependency
|
|
21
|
+
s.add_dependency 'devise', '>= 3.4.1'
|
|
22
|
+
s.add_dependency 'net-ldap', '>= 0.16.0'
|
|
23
23
|
|
|
24
|
-
s.add_development_dependency
|
|
25
|
-
s.add_development_dependency
|
|
26
|
-
s.add_development_dependency
|
|
27
|
-
s.add_development_dependency
|
|
28
|
-
s.add_development_dependency
|
|
29
|
-
s.add_development_dependency
|
|
30
|
-
s.add_development_dependency
|
|
24
|
+
s.add_development_dependency 'rake', '>= 0.9'
|
|
25
|
+
s.add_development_dependency 'rdoc', '>= 3'
|
|
26
|
+
s.add_development_dependency 'rails', '>= 4.0'
|
|
27
|
+
s.add_development_dependency 'sqlite3'
|
|
28
|
+
s.add_development_dependency 'factory_girl_rails', '~> 1.0'
|
|
29
|
+
s.add_development_dependency 'factory_girl', '~> 2.0'
|
|
30
|
+
s.add_development_dependency 'rspec-rails'
|
|
31
31
|
|
|
32
32
|
%w{database_cleaner capybara launchy}.each do |dep|
|
|
33
33
|
s.add_development_dependency(dep)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
require 'devise'
|
|
3
|
+
require 'net/ldap'
|
|
3
4
|
|
|
4
5
|
require 'devise_ldap_authenticatable/exception'
|
|
5
6
|
require 'devise_ldap_authenticatable/logger'
|
|
@@ -11,31 +12,40 @@ module Devise
|
|
|
11
12
|
# Allow logging
|
|
12
13
|
mattr_accessor :ldap_logger
|
|
13
14
|
@@ldap_logger = true
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
# Add valid users to database
|
|
16
17
|
mattr_accessor :ldap_create_user
|
|
17
18
|
@@ldap_create_user = false
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
# A path to YAML config file or a Proc that returns a
|
|
20
21
|
# configuration hash
|
|
21
22
|
mattr_accessor :ldap_config
|
|
22
23
|
# @@ldap_config = "#{Rails.root}/config/ldap.yml"
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
mattr_accessor :ldap_update_password
|
|
25
26
|
@@ldap_update_password = true
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
mattr_accessor :ldap_check_group_membership
|
|
28
29
|
@@ldap_check_group_membership = false
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
mattr_accessor :ldap_check_group_membership_without_admin
|
|
32
|
+
@@ldap_check_group_membership_without_admin = false
|
|
33
|
+
|
|
30
34
|
mattr_accessor :ldap_check_attributes
|
|
31
35
|
@@ldap_check_role_attribute = false
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
mattr_accessor :ldap_check_attributes_presence
|
|
38
|
+
@@ldap_check_attributes_presence = false
|
|
39
|
+
|
|
33
40
|
mattr_accessor :ldap_use_admin_to_bind
|
|
34
41
|
@@ldap_use_admin_to_bind = false
|
|
35
|
-
|
|
42
|
+
|
|
36
43
|
mattr_accessor :ldap_auth_username_builder
|
|
37
44
|
@@ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "#{attribute}=#{login},#{ldap.base}" }
|
|
38
45
|
|
|
46
|
+
mattr_accessor :ldap_auth_password_builder
|
|
47
|
+
@@ldap_auth_password_builder = Proc.new() {|new_password| Net::LDAP::Password.generate(:sha, new_password) }
|
|
48
|
+
|
|
39
49
|
mattr_accessor :ldap_ad_group_check
|
|
40
50
|
@@ldap_ad_group_check = false
|
|
41
51
|
end
|
|
@@ -15,6 +15,16 @@ module Devise
|
|
|
15
15
|
resource.authorized?
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def self.expired_valid_credentials?(login, password_plaintext)
|
|
19
|
+
options = {:login => login,
|
|
20
|
+
:password => password_plaintext,
|
|
21
|
+
:ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
|
|
22
|
+
:admin => ::Devise.ldap_use_admin_to_bind}
|
|
23
|
+
|
|
24
|
+
resource = Devise::LDAP::Connection.new(options)
|
|
25
|
+
resource.expired_valid_credentials?
|
|
26
|
+
end
|
|
27
|
+
|
|
18
28
|
def self.update_password(login, new_password)
|
|
19
29
|
options = {:login => login,
|
|
20
30
|
:new_password => new_password,
|
|
@@ -34,7 +44,7 @@ module Devise
|
|
|
34
44
|
:ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
|
|
35
45
|
:admin => ::Devise.ldap_use_admin_to_bind}
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
Devise::LDAP::Connection.new(options)
|
|
38
48
|
end
|
|
39
49
|
|
|
40
50
|
def self.valid_login?(login)
|
|
@@ -54,18 +64,18 @@ module Devise
|
|
|
54
64
|
end
|
|
55
65
|
|
|
56
66
|
def self.set_ldap_param(login, param, new_value, password = nil)
|
|
57
|
-
options = {
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
options = {:login => login,
|
|
68
|
+
:ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
|
|
69
|
+
:password => password }
|
|
60
70
|
|
|
61
71
|
resource = Devise::LDAP::Connection.new(options)
|
|
62
72
|
resource.set_param(param, new_value)
|
|
63
73
|
end
|
|
64
74
|
|
|
65
75
|
def self.delete_ldap_param(login, param, password = nil)
|
|
66
|
-
options = {
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
options = {:login => login,
|
|
77
|
+
:ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
|
|
78
|
+
:password => password }
|
|
69
79
|
|
|
70
80
|
resource = Devise::LDAP::Connection.new(options)
|
|
71
81
|
resource.delete_param(param)
|
|
@@ -79,9 +89,6 @@ module Devise
|
|
|
79
89
|
def self.get_ldap_entry(login)
|
|
80
90
|
self.ldap_connect(login).search_for_login
|
|
81
91
|
end
|
|
82
|
-
|
|
83
92
|
end
|
|
84
|
-
|
|
85
93
|
end
|
|
86
|
-
|
|
87
94
|
end
|
|
@@ -24,11 +24,13 @@ module Devise
|
|
|
24
24
|
|
|
25
25
|
@group_base = ldap_config["group_base"]
|
|
26
26
|
@check_group_membership = ldap_config.has_key?("check_group_membership") ? ldap_config["check_group_membership"] : ::Devise.ldap_check_group_membership
|
|
27
|
+
@check_group_membership_without_admin = ldap_config.has_key?("check_group_membership_without_admin") ? ldap_config["check_group_membership_without_admin"] : ::Devise.ldap_check_group_membership_without_admin
|
|
27
28
|
@required_groups = ldap_config["required_groups"]
|
|
29
|
+
@group_membership_attribute = ldap_config.has_key?("group_membership_attribute") ? ldap_config["group_membership_attribute"] : "uniqueMember"
|
|
28
30
|
@required_attributes = ldap_config["require_attribute"]
|
|
31
|
+
@required_attributes_presence = ldap_config["require_attribute_presence"]
|
|
29
32
|
|
|
30
33
|
@ldap.auth ldap_config["admin_user"], ldap_config["admin_password"] if params[:admin]
|
|
31
|
-
@ldap.auth params[:login], params[:password] if ldap_config["admin_as_user"]
|
|
32
34
|
|
|
33
35
|
@login = params[:login]
|
|
34
36
|
@password = params[:password]
|
|
@@ -83,10 +85,25 @@ module Devise
|
|
|
83
85
|
authenticate!
|
|
84
86
|
end
|
|
85
87
|
|
|
88
|
+
def last_message_bad_credentials?
|
|
89
|
+
@ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 52e'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def last_message_expired_credentials?
|
|
93
|
+
@ldap.get_operation_result.error_message.to_s.include? 'AcceptSecurityContext error, data 773'
|
|
94
|
+
end
|
|
95
|
+
|
|
86
96
|
def authorized?
|
|
87
97
|
DeviseLdapAuthenticatable::Logger.send("Authorizing user #{dn}")
|
|
88
98
|
if !authenticated?
|
|
89
|
-
|
|
99
|
+
if last_message_bad_credentials?
|
|
100
|
+
DeviseLdapAuthenticatable::Logger.send("Not authorized because of invalid credentials.")
|
|
101
|
+
elsif last_message_expired_credentials?
|
|
102
|
+
DeviseLdapAuthenticatable::Logger.send("Not authorized because of expired credentials.")
|
|
103
|
+
else
|
|
104
|
+
DeviseLdapAuthenticatable::Logger.send("Not authorized because not authenticated.")
|
|
105
|
+
end
|
|
106
|
+
|
|
90
107
|
return false
|
|
91
108
|
elsif !in_required_groups?
|
|
92
109
|
DeviseLdapAuthenticatable::Logger.send("Not authorized because not in required groups.")
|
|
@@ -94,17 +111,26 @@ module Devise
|
|
|
94
111
|
elsif !has_required_attribute?
|
|
95
112
|
DeviseLdapAuthenticatable::Logger.send("Not authorized because does not have required attribute.")
|
|
96
113
|
return false
|
|
114
|
+
elsif !has_required_attribute_presence?
|
|
115
|
+
DeviseLdapAuthenticatable::Logger.send("Not authorized because does not have required attribute present.")
|
|
116
|
+
return false
|
|
97
117
|
else
|
|
98
118
|
return true
|
|
99
119
|
end
|
|
100
120
|
end
|
|
101
121
|
|
|
122
|
+
def expired_valid_credentials?
|
|
123
|
+
DeviseLdapAuthenticatable::Logger.send("Authorizing user #{dn}")
|
|
124
|
+
|
|
125
|
+
!authenticated? && last_message_expired_credentials?
|
|
126
|
+
end
|
|
127
|
+
|
|
102
128
|
def change_password!
|
|
103
|
-
update_ldap(:
|
|
129
|
+
update_ldap(:userPassword => ::Devise.ldap_auth_password_builder.call(@new_password))
|
|
104
130
|
end
|
|
105
131
|
|
|
106
132
|
def in_required_groups?
|
|
107
|
-
return true unless @check_group_membership
|
|
133
|
+
return true unless @check_group_membership || @check_group_membership_without_admin
|
|
108
134
|
|
|
109
135
|
## FIXME set errors here, the ldap.yml isn't set properly.
|
|
110
136
|
return false if @required_groups.nil?
|
|
@@ -122,23 +148,29 @@ module Devise
|
|
|
122
148
|
def in_group?(group_name, group_attribute = LDAP::DEFAULT_GROUP_UNIQUE_MEMBER_LIST_KEY)
|
|
123
149
|
in_group = false
|
|
124
150
|
|
|
125
|
-
|
|
151
|
+
if @check_group_membership_without_admin
|
|
152
|
+
group_checking_ldap = @ldap
|
|
153
|
+
else
|
|
154
|
+
group_checking_ldap = Connection.admin
|
|
155
|
+
end
|
|
126
156
|
|
|
127
157
|
unless ::Devise.ldap_ad_group_check
|
|
128
|
-
|
|
158
|
+
group_checking_ldap.search(:base => group_name, :scope => Net::LDAP::SearchScope_BaseObject) do |entry|
|
|
129
159
|
if entry[group_attribute].include? dn
|
|
130
160
|
in_group = true
|
|
161
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} IS included in group: #{group_name}")
|
|
131
162
|
end
|
|
132
163
|
end
|
|
133
164
|
else
|
|
134
165
|
# AD optimization - extension will recursively check sub-groups with one query
|
|
135
166
|
# "(memberof:1.2.840.113556.1.4.1941:=group_name)"
|
|
136
|
-
search_result =
|
|
167
|
+
search_result = group_checking_ldap.search(:base => dn,
|
|
137
168
|
:filter => Net::LDAP::Filter.ex("memberof:1.2.840.113556.1.4.1941", group_name),
|
|
138
169
|
:scope => Net::LDAP::SearchScope_BaseObject)
|
|
139
170
|
# Will return the user entry if belongs to group otherwise nothing
|
|
140
171
|
if search_result.length == 1 && search_result[0].dn.eql?(dn)
|
|
141
172
|
in_group = true
|
|
173
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} IS included in group: #{group_name}")
|
|
142
174
|
end
|
|
143
175
|
end
|
|
144
176
|
|
|
@@ -153,11 +185,11 @@ module Devise
|
|
|
153
185
|
return true unless ::Devise.ldap_check_attributes
|
|
154
186
|
|
|
155
187
|
admin_ldap = Connection.admin
|
|
156
|
-
|
|
157
188
|
user = find_ldap_user(admin_ldap)
|
|
158
189
|
|
|
159
190
|
@required_attributes.each do |key,val|
|
|
160
|
-
|
|
191
|
+
matching_attributes = user[key] & Array(val)
|
|
192
|
+
unless (matching_attributes).any?
|
|
161
193
|
DeviseLdapAuthenticatable::Logger.send("User #{dn} did not match attribute #{key}:#{val}")
|
|
162
194
|
return false
|
|
163
195
|
end
|
|
@@ -166,11 +198,28 @@ module Devise
|
|
|
166
198
|
return true
|
|
167
199
|
end
|
|
168
200
|
|
|
201
|
+
def has_required_attribute_presence?
|
|
202
|
+
return true unless ::Devise.ldap_check_attributes_presence
|
|
203
|
+
|
|
204
|
+
user = search_for_login
|
|
205
|
+
|
|
206
|
+
@required_attributes_presence.each do |key,val|
|
|
207
|
+
if val && !user.attribute_names.include?(key.to_sym)
|
|
208
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} doesn't include attribute #{key}")
|
|
209
|
+
return false
|
|
210
|
+
elsif !val && user.attribute_names.include?(key.to_sym)
|
|
211
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} includes attribute #{key}")
|
|
212
|
+
return false
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
return true
|
|
217
|
+
end
|
|
218
|
+
|
|
169
219
|
def user_groups
|
|
170
220
|
admin_ldap = Connection.admin
|
|
171
|
-
|
|
172
221
|
DeviseLdapAuthenticatable::Logger.send("Getting groups for #{dn}")
|
|
173
|
-
filter = Net::LDAP::Filter.eq(
|
|
222
|
+
filter = Net::LDAP::Filter.eq(@group_membership_attribute, dn)
|
|
174
223
|
admin_ldap.search(:filter => filter, :base => @group_base).collect(&:dn)
|
|
175
224
|
end
|
|
176
225
|
|
|
@@ -188,6 +237,10 @@ module Devise
|
|
|
188
237
|
ldap_entry = nil
|
|
189
238
|
match_count = 0
|
|
190
239
|
@ldap.search(:filter => filter) {|entry| ldap_entry = entry; match_count+=1}
|
|
240
|
+
op_result= @ldap.get_operation_result
|
|
241
|
+
if op_result.code!=0 then
|
|
242
|
+
DeviseLdapAuthenticatable::Logger.send("LDAP Error #{op_result.code}: #{op_result.message}")
|
|
243
|
+
end
|
|
191
244
|
DeviseLdapAuthenticatable::Logger.send("LDAP search yielded #{match_count} matches")
|
|
192
245
|
ldap_entry
|
|
193
246
|
end
|
|
@@ -53,7 +53,7 @@ module Devise
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def ldap_groups
|
|
56
|
-
Devise::LDAP::Adapter.get_groups(login_with)
|
|
56
|
+
@ldap_groups ||= Devise::LDAP::Adapter.get_groups(login_with)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def in_ldap_group?(group_name, group_attribute = LDAP::DEFAULT_GROUP_UNIQUE_MEMBER_LIST_KEY)
|
|
@@ -10,7 +10,7 @@ module Devise
|
|
|
10
10
|
# indicating whether the resource is not found in the database or the credentials
|
|
11
11
|
# are invalid.
|
|
12
12
|
def authenticate!
|
|
13
|
-
resource = mapping.to.find_for_ldap_authentication(authentication_hash.merge(password
|
|
13
|
+
resource = mapping.to.find_for_ldap_authentication(authentication_hash.merge(:password => password))
|
|
14
14
|
|
|
15
15
|
return fail(:invalid) unless resource
|
|
16
16
|
|
|
@@ -13,7 +13,7 @@ module DeviseLdapAuthenticatable
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def create_default_devise_settings
|
|
16
|
-
inject_into_file "config/initializers/devise.rb", default_devise_settings, :after => "Devise.setup do |config|\n"
|
|
16
|
+
inject_into_file "config/initializers/devise.rb", default_devise_settings, :after => "Devise.setup do |config|\n"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def update_user_model
|
|
@@ -28,7 +28,7 @@ module DeviseLdapAuthenticatable
|
|
|
28
28
|
|
|
29
29
|
def default_devise_settings
|
|
30
30
|
settings = <<-eof
|
|
31
|
-
# ==> LDAP Configuration
|
|
31
|
+
# ==> LDAP Configuration
|
|
32
32
|
# config.ldap_logger = true
|
|
33
33
|
# config.ldap_create_user = false
|
|
34
34
|
# config.ldap_update_password = true
|
|
@@ -36,12 +36,13 @@ module DeviseLdapAuthenticatable
|
|
|
36
36
|
# config.ldap_check_group_membership = false
|
|
37
37
|
# config.ldap_check_group_membership_without_admin = false
|
|
38
38
|
# config.ldap_check_attributes = false
|
|
39
|
+
# config.ldap_check_attributes_presence = false
|
|
39
40
|
# config.ldap_use_admin_to_bind = false
|
|
40
41
|
# config.ldap_ad_group_check = false
|
|
41
42
|
|
|
42
43
|
eof
|
|
43
|
-
if options.advanced?
|
|
44
|
-
settings << <<-eof
|
|
44
|
+
if options.advanced?
|
|
45
|
+
settings << <<-eof
|
|
45
46
|
# ==> Advanced LDAP Configuration
|
|
46
47
|
# config.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "\#{attribute}=\#{login},\#{ldap.base}" }
|
|
47
48
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
## Authorizations
|
|
2
2
|
# Uncomment out the merging for each environment that you'd like to include.
|
|
3
3
|
# You can also just copy and paste the tree (do not include the "authorizations") to each
|
|
4
|
-
# environment if you need something different per
|
|
4
|
+
# environment if you need something different per environment.
|
|
5
5
|
authorizations: &AUTHORIZATIONS
|
|
6
6
|
allow_unauthenticated_bind: false
|
|
7
7
|
group_base: ou=groups,dc=test,dc=com
|
|
@@ -18,6 +18,12 @@ authorizations: &AUTHORIZATIONS
|
|
|
18
18
|
require_attribute:
|
|
19
19
|
objectClass: inetOrgPerson
|
|
20
20
|
authorizationRole: postsAdmin
|
|
21
|
+
## Requires config.ldap_check_attributes_presence in devise.rb to be true
|
|
22
|
+
## Can have multiple attributes set to true or false to check presence, all must match all to be authorized
|
|
23
|
+
require_attribute_presence:
|
|
24
|
+
mail: true
|
|
25
|
+
telephoneNumber: true
|
|
26
|
+
serviceAccount: false
|
|
21
27
|
|
|
22
28
|
## Environment
|
|
23
29
|
|
|
@@ -7,7 +7,9 @@ authorizations: &AUTHORIZATIONS
|
|
|
7
7
|
require_attribute:
|
|
8
8
|
objectClass: inetOrgPerson
|
|
9
9
|
authorizationRole: blogAdmin
|
|
10
|
-
|
|
10
|
+
require_attribute_presence:
|
|
11
|
+
mail: true
|
|
12
|
+
|
|
11
13
|
test: &TEST
|
|
12
14
|
host: localhost
|
|
13
15
|
port: 3389
|
|
@@ -17,6 +19,6 @@ test: &TEST
|
|
|
17
19
|
admin_password: secret
|
|
18
20
|
ssl: false
|
|
19
21
|
<<: *AUTHORIZATIONS
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
development:
|
|
22
24
|
<<: *TEST
|
|
@@ -6,9 +6,11 @@ authorizations: &AUTHORIZATIONS
|
|
|
6
6
|
required_groups:
|
|
7
7
|
- cn=admins,<%= "ou=groups,#{@base}" %>
|
|
8
8
|
require_attribute:
|
|
9
|
-
objectClass:
|
|
9
|
+
objectClass:
|
|
10
|
+
- inetOrgPerson
|
|
11
|
+
- organizationalPerson
|
|
10
12
|
authorizationRole: blogAdmin
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
test: &TEST
|
|
13
15
|
host: <%= "localhost" %>
|
|
14
16
|
port: 3389
|
|
@@ -18,6 +20,6 @@ test: &TEST
|
|
|
18
20
|
admin_password: secret
|
|
19
21
|
ssl: false
|
|
20
22
|
<<: *AUTHORIZATIONS
|
|
21
|
-
|
|
23
|
+
|
|
22
24
|
development:
|
|
23
25
|
<<: *TEST
|
data/spec/spec_helper.rb
CHANGED
|
@@ -2,7 +2,6 @@ ENV["RAILS_ENV"] = "test"
|
|
|
2
2
|
|
|
3
3
|
require File.expand_path("rails_app/config/environment.rb", File.dirname(__FILE__))
|
|
4
4
|
require 'rspec/rails'
|
|
5
|
-
require 'rspec/autorun'
|
|
6
5
|
require 'factory_girl' # not sure why this is not already required
|
|
7
6
|
|
|
8
7
|
# Rails 4.1 and RSpec are a bit on different pages on who should run migrations
|
|
@@ -50,6 +49,7 @@ def default_devise_settings!
|
|
|
50
49
|
::Devise.ldap_config = "#{Rails.root}/config/#{"ssl_" if ENV["LDAP_SSL"]}ldap.yml"
|
|
51
50
|
::Devise.ldap_check_group_membership = false
|
|
52
51
|
::Devise.ldap_check_attributes = false
|
|
52
|
+
::Devise.ldap_check_attributes_presence = false
|
|
53
53
|
::Devise.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "#{attribute}=#{login},#{ldap.base}" }
|
|
54
54
|
::Devise.authentication_keys = [:email]
|
|
55
55
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Devise::LDAP::Adapter do
|
|
4
|
+
describe '#expired_valid_credentials?' do
|
|
5
|
+
before do
|
|
6
|
+
::Devise.ldap_use_admin_to_bind = true
|
|
7
|
+
expect_any_instance_of(Devise::LDAP::Connection).to receive(:expired_valid_credentials?)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'can bind as the admin user' do
|
|
11
|
+
expect(Devise::LDAP::Connection).to receive(:new)
|
|
12
|
+
.with(hash_including(
|
|
13
|
+
:login => 'test.user@test.com',
|
|
14
|
+
:password => 'pass',
|
|
15
|
+
:ldap_auth_username_builder => kind_of(Proc),
|
|
16
|
+
:admin => true)).and_call_original
|
|
17
|
+
|
|
18
|
+
Devise::LDAP::Adapter.expired_valid_credentials?('test.user@test.com', 'pass')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -11,4 +11,93 @@ describe 'Connection' do
|
|
|
11
11
|
connection = Devise::LDAP::Connection.new()
|
|
12
12
|
expect(connection.ldap.base).to eq('ou=testbase,dc=test,dc=com')
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
class TestOpResult
|
|
16
|
+
attr_accessor :error_message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe '#expired_valid_credentials?' do
|
|
20
|
+
let(:conn) { double(Net::LDAP).as_null_object }
|
|
21
|
+
let(:error) { }
|
|
22
|
+
let(:is_authed) { false }
|
|
23
|
+
before do
|
|
24
|
+
expect(Net::LDAP).to receive(:new).and_return(conn)
|
|
25
|
+
allow(conn).to receive(:get_operation_result).and_return(TestOpResult.new.tap{|r| r.error_message = error})
|
|
26
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:authenticated?).and_return(is_authed)
|
|
27
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:dn).and_return('any dn')
|
|
28
|
+
expect(DeviseLdapAuthenticatable::Logger).to receive(:send).with('Authorizing user any dn')
|
|
29
|
+
end
|
|
30
|
+
subject do
|
|
31
|
+
Devise::LDAP::Connection.new.expired_valid_credentials?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context do
|
|
35
|
+
let(:error) { 'THIS PART CAN BE ANYTHING AcceptSecurityContext error, data 773 SO CAN THIS' }
|
|
36
|
+
it 'is true when expired credential error is returned and not already authenticated' do
|
|
37
|
+
expect(subject).to be true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context do
|
|
42
|
+
it 'is false when expired credential error is not returned and not already authenticated' do
|
|
43
|
+
expect(subject).to be false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context do
|
|
48
|
+
let(:is_authed) { true }
|
|
49
|
+
it 'is false when expired credential error is not returned and already authenticated' do
|
|
50
|
+
expect(subject).to be false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe '#authorized?' do
|
|
56
|
+
let(:conn) { double(Net::LDAP).as_null_object }
|
|
57
|
+
let(:error) { }
|
|
58
|
+
let(:log_message) { }
|
|
59
|
+
let(:is_authed) { false }
|
|
60
|
+
before do
|
|
61
|
+
expect(Net::LDAP).to receive(:new).and_return(conn)
|
|
62
|
+
allow(conn).to receive(:get_operation_result).and_return(TestOpResult.new.tap{|r| r.error_message = error})
|
|
63
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:authenticated?).and_return(is_authed)
|
|
64
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:dn).and_return('any dn')
|
|
65
|
+
expect(DeviseLdapAuthenticatable::Logger).to receive(:send).with('Authorizing user any dn')
|
|
66
|
+
end
|
|
67
|
+
subject do
|
|
68
|
+
Devise::LDAP::Connection.new.authorized?
|
|
69
|
+
end
|
|
70
|
+
context do
|
|
71
|
+
before { expect(DeviseLdapAuthenticatable::Logger).to receive(:send).with(log_message) }
|
|
72
|
+
|
|
73
|
+
context do
|
|
74
|
+
let(:error) { 'THIS PART CAN BE ANYTHING AcceptSecurityContext error, data 52e SO CAN THIS' }
|
|
75
|
+
let(:log_message) { 'Not authorized because of invalid credentials.' }
|
|
76
|
+
it 'is false when credential error is returned' do
|
|
77
|
+
expect(subject).to be false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
context do
|
|
81
|
+
let(:error) { 'THIS PART CAN BE ANYTHING AcceptSecurityContext error, data 773 SO CAN THIS' }
|
|
82
|
+
let(:log_message) { 'Not authorized because of expired credentials.' }
|
|
83
|
+
it 'is false when expired error is returned' do
|
|
84
|
+
expect(subject).to be false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
context do
|
|
88
|
+
let(:error) { 'any error' }
|
|
89
|
+
let(:log_message) { 'Not authorized because not authenticated.' }
|
|
90
|
+
it 'is false when any other error is returned' do
|
|
91
|
+
expect(subject).to be false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context do
|
|
97
|
+
let(:is_authed) { true }
|
|
98
|
+
it 'is true when already authenticated' do
|
|
99
|
+
expect(subject).to be true
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
14
103
|
end
|
data/spec/unit/user_spec.rb
CHANGED
|
@@ -16,7 +16,7 @@ describe 'Users' do
|
|
|
16
16
|
reset_ldap_server!
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
describe "look up
|
|
19
|
+
describe "look up an ldap user" do
|
|
20
20
|
it "should return true for a user that does exist in LDAP" do
|
|
21
21
|
assert_equal true, ::Devise::LDAP::Adapter.valid_login?('example.user@test.com')
|
|
22
22
|
end
|
|
@@ -48,7 +48,7 @@ describe 'Users' do
|
|
|
48
48
|
should_be_validated @user, "secret"
|
|
49
49
|
@user.password = "changed"
|
|
50
50
|
@user.change_password!("secret")
|
|
51
|
-
should_be_validated @user, "changed", "password was not changed properly on the LDAP
|
|
51
|
+
should_be_validated @user, "changed", "password was not changed properly on the LDAP server"
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
it "should not allow to change password if setting is false" do
|
|
@@ -80,7 +80,7 @@ describe 'Users' do
|
|
|
80
80
|
it "should create a user in the database" do
|
|
81
81
|
@user = User.find_for_ldap_authentication(:email => "example.user@test.com", :password => "secret")
|
|
82
82
|
assert_equal(User.all.size, 1)
|
|
83
|
-
User.all.collect(&:email).
|
|
83
|
+
expect(User.all.collect(&:email)).to include("example.user@test.com")
|
|
84
84
|
assert(@user.persisted?)
|
|
85
85
|
end
|
|
86
86
|
|
|
@@ -116,7 +116,7 @@ describe 'Users' do
|
|
|
116
116
|
::Devise.case_insensitive_keys = [:email]
|
|
117
117
|
|
|
118
118
|
@user = User.find_for_ldap_authentication(:email => "EXAMPLE.user@test.com", :password => "secret")
|
|
119
|
-
User.all.collect(&:email).
|
|
119
|
+
expect(User.all.collect(&:email)).to include("example.user@test.com")
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
|
|
@@ -135,32 +135,32 @@ describe 'Users' do
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
it "should admin should have the proper groups set" do
|
|
138
|
-
@admin.ldap_groups.
|
|
138
|
+
expect(@admin.ldap_groups).to include('cn=admins,ou=groups,dc=test,dc=com')
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
it "should user should not be allowed in" do
|
|
142
142
|
should_not_be_validated @user, "secret"
|
|
143
143
|
end
|
|
144
144
|
end
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
describe "check group membership" do
|
|
147
147
|
before do
|
|
148
148
|
@admin = Factory.create(:admin)
|
|
149
149
|
@user = Factory.create(:user)
|
|
150
150
|
end
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
it "should return true for admin being in the admins group" do
|
|
153
153
|
assert_equal true, @admin.in_ldap_group?('cn=admins,ou=groups,dc=test,dc=com')
|
|
154
154
|
end
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
it "should return false for admin being in the admins group using the 'foobar' group attribute" do
|
|
157
157
|
assert_equal false, @admin.in_ldap_group?('cn=admins,ou=groups,dc=test,dc=com', 'foobar')
|
|
158
158
|
end
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
it "should return true for user being in the users group" do
|
|
161
161
|
assert_equal true, @user.in_ldap_group?('cn=users,ou=groups,dc=test,dc=com')
|
|
162
|
-
end
|
|
163
|
-
|
|
162
|
+
end
|
|
163
|
+
|
|
164
164
|
it "should return false for user being in the admins group" do
|
|
165
165
|
assert_equal false, @user.in_ldap_group?('cn=admins,ou=groups,dc=test,dc=com')
|
|
166
166
|
end
|
|
@@ -217,6 +217,26 @@ describe 'Users' do
|
|
|
217
217
|
end
|
|
218
218
|
end
|
|
219
219
|
|
|
220
|
+
describe "use attribute presence for authorization" do
|
|
221
|
+
before do
|
|
222
|
+
@admin = Factory.create(:admin)
|
|
223
|
+
@user = Factory.create(:user)
|
|
224
|
+
::Devise.ldap_check_attributes_presence = true
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
after do
|
|
228
|
+
::Devise.ldap_check_attributes_presence = false
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it "should admin should not be allowed in" do
|
|
232
|
+
should_not_be_validated @admin, "admin_secret"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "should user should be allowed in" do
|
|
236
|
+
should_be_validated @user, "secret"
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
220
240
|
describe "use admin setting to bind" do
|
|
221
241
|
before do
|
|
222
242
|
@admin = Factory.create(:admin)
|
|
@@ -229,6 +249,19 @@ describe 'Users' do
|
|
|
229
249
|
end
|
|
230
250
|
end
|
|
231
251
|
|
|
252
|
+
describe 'check password expiration' do
|
|
253
|
+
before { allow_any_instance_of(Devise::LDAP::Connection).to receive(:authenticated?).and_return(false) }
|
|
254
|
+
|
|
255
|
+
it 'should return false for a user that has a fresh password' do
|
|
256
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:last_message_expired_credentials?).and_return(false)
|
|
257
|
+
assert_equal false, ::Devise::LDAP::Adapter.expired_valid_credentials?('example.user@test.com','secret')
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it 'should return true for a user that has an expired password' do
|
|
261
|
+
allow_any_instance_of(Devise::LDAP::Connection).to receive(:last_message_expired_credentials?).and_return(true)
|
|
262
|
+
assert_equal true, ::Devise::LDAP::Adapter.expired_valid_credentials?('example.user@test.com','secret')
|
|
263
|
+
end
|
|
264
|
+
end
|
|
232
265
|
end
|
|
233
266
|
|
|
234
267
|
describe "use uid for login" do
|
|
@@ -259,7 +292,7 @@ describe 'Users' do
|
|
|
259
292
|
it "should create a user in the database" do
|
|
260
293
|
@user = User.find_for_ldap_authentication(:uid => "example_user", :password => "secret")
|
|
261
294
|
assert_equal(User.all.size, 1)
|
|
262
|
-
User.all.collect(&:uid).
|
|
295
|
+
expect(User.all.collect(&:uid)).to include("example_user")
|
|
263
296
|
end
|
|
264
297
|
|
|
265
298
|
it "should call ldap_before_save hooks" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise_ldap_authenticatable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Curtis Schiewek
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2018-02-13 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: devise
|
|
@@ -32,20 +32,14 @@ dependencies:
|
|
|
32
32
|
requirements:
|
|
33
33
|
- - ">="
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
|
-
version: 0.
|
|
36
|
-
- - "<="
|
|
37
|
-
- !ruby/object:Gem::Version
|
|
38
|
-
version: '0.11'
|
|
35
|
+
version: 0.16.0
|
|
39
36
|
type: :runtime
|
|
40
37
|
prerelease: false
|
|
41
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
39
|
requirements:
|
|
43
40
|
- - ">="
|
|
44
41
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 0.
|
|
46
|
-
- - "<="
|
|
47
|
-
- !ruby/object:Gem::Version
|
|
48
|
-
version: '0.11'
|
|
42
|
+
version: 0.16.0
|
|
49
43
|
- !ruby/object:Gem::Dependency
|
|
50
44
|
name: rake
|
|
51
45
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -277,6 +271,7 @@ files:
|
|
|
277
271
|
- spec/rails_app/script/rails
|
|
278
272
|
- spec/spec_helper.rb
|
|
279
273
|
- spec/support/factories.rb
|
|
274
|
+
- spec/unit/adapter_spec.rb
|
|
280
275
|
- spec/unit/connection_spec.rb
|
|
281
276
|
- spec/unit/user_spec.rb
|
|
282
277
|
homepage: https://github.com/cschiewek/devise_ldap_authenticatable
|
|
@@ -299,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
299
294
|
version: '0'
|
|
300
295
|
requirements: []
|
|
301
296
|
rubyforge_project:
|
|
302
|
-
rubygems_version: 2.
|
|
297
|
+
rubygems_version: 2.6.11
|
|
303
298
|
signing_key:
|
|
304
299
|
specification_version: 4
|
|
305
300
|
summary: Devise extension to allow authentication via LDAP
|
|
@@ -372,5 +367,6 @@ test_files:
|
|
|
372
367
|
- spec/rails_app/script/rails
|
|
373
368
|
- spec/spec_helper.rb
|
|
374
369
|
- spec/support/factories.rb
|
|
370
|
+
- spec/unit/adapter_spec.rb
|
|
375
371
|
- spec/unit/connection_spec.rb
|
|
376
372
|
- spec/unit/user_spec.rb
|