devise_ldap_authenticatable 0.3.5 → 0.4.0
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.
- data/README.md +25 -7
- data/lib/devise_ldap_authenticatable.rb +12 -0
- data/lib/devise_ldap_authenticatable/exception.rb +6 -0
- data/lib/devise_ldap_authenticatable/ldap_adapter.rb +109 -29
- data/lib/devise_ldap_authenticatable/logger.rb +11 -0
- data/lib/devise_ldap_authenticatable/model.rb +15 -6
- data/lib/devise_ldap_authenticatable/version.rb +1 -1
- data/lib/generators/devise_ldap_authenticatable/install_generator.rb +16 -0
- data/lib/generators/devise_ldap_authenticatable/templates/ldap.yml +28 -7
- metadata +10 -9
data/README.md
CHANGED
@@ -12,8 +12,11 @@ Requirements
|
|
12
12
|
|
13
13
|
- An LDAP server (tested on OpenLDAP)
|
14
14
|
- Rails 3.0.0.beta4
|
15
|
+
|
16
|
+
These gems are dependencies of the gem:
|
17
|
+
|
15
18
|
- Devise 1.1.rc2
|
16
|
-
-
|
19
|
+
- net-ldap 0.1.1
|
17
20
|
|
18
21
|
Installation
|
19
22
|
------------
|
@@ -25,12 +28,13 @@ This will *only* work for Rails 3 applications.
|
|
25
28
|
In the Gemfile for your application:
|
26
29
|
|
27
30
|
gem "devise", "1.1.rc2"
|
28
|
-
gem "devise_ldap_authenticatable", "0.
|
29
|
-
|
30
|
-
|
31
|
+
gem "devise_ldap_authenticatable", "0.4.0"
|
32
|
+
|
33
|
+
To get the latest version, pull directly from github instead of the gem:
|
31
34
|
|
32
35
|
gem "devise_ldap_authenticatable", :git => "git://github.com/cschiewek/devise_ldap_authenticatable.git", :branch => "rails3"
|
33
36
|
|
37
|
+
|
34
38
|
Setup
|
35
39
|
-----
|
36
40
|
|
@@ -44,6 +48,9 @@ This will install the sample.yml, update the devise.rb initializer, and update y
|
|
44
48
|
# Default: user
|
45
49
|
[--update-model] # Update model to change from database_authenticatable to ldap_authenticatable
|
46
50
|
# Default: true
|
51
|
+
[--add-rescue] # Update Application Controller with resuce_from for DeviseLdapAuthenticatable::LdapException
|
52
|
+
# Default: true
|
53
|
+
|
47
54
|
|
48
55
|
|
49
56
|
Usage
|
@@ -61,16 +68,27 @@ Configuration
|
|
61
68
|
|
62
69
|
In initializer `config/initializers/devise.rb` :
|
63
70
|
|
64
|
-
* ldap\
|
71
|
+
* ldap\_logger _(default: true)_
|
72
|
+
* If set to true, will log LDAP queries to the Rails logger.
|
73
|
+
|
74
|
+
* ldap\_create\_user _(default: false)_
|
65
75
|
* If set to true, all valid LDAP users will be allowed to login and an appropriate user record will be created.
|
66
76
|
If set to false, you will have to create the user record before they will be allowed to login.
|
67
77
|
|
68
|
-
* ldap\_config
|
78
|
+
* ldap\_config _(default: #{Rails.root}/config/ldap.yml)_
|
69
79
|
* Where to find the LDAP config file. Commented out to use the default, change if needed.
|
70
80
|
|
71
|
-
* ldap\_update\_password
|
81
|
+
* ldap\_update\_password _(default: true)_
|
72
82
|
* When doing password resets, if true will update the LDAP server. Requires admin password in the ldap.yml
|
73
83
|
|
84
|
+
|
85
|
+
* ldap\_check\_group_membership _(default: false)_
|
86
|
+
* 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.
|
87
|
+
|
88
|
+
|
89
|
+
* ldap\_check\_attributes _(default: false)_
|
90
|
+
* When set to true, the user trying to login will be checked to make sure they have all of the attributes in the ldap.yml file.
|
91
|
+
|
74
92
|
Testing
|
75
93
|
-------
|
76
94
|
|
@@ -1,12 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'devise'
|
3
3
|
|
4
|
+
require 'devise_ldap_authenticatable/exception'
|
5
|
+
require 'devise_ldap_authenticatable/logger'
|
4
6
|
require 'devise_ldap_authenticatable/schema'
|
5
7
|
require 'devise_ldap_authenticatable/ldap_adapter'
|
6
8
|
require 'devise_ldap_authenticatable/routes'
|
7
9
|
|
8
10
|
# Get ldap information from config/ldap.yml now
|
9
11
|
module Devise
|
12
|
+
# Allow logging
|
13
|
+
mattr_accessor :ldap_logger
|
14
|
+
@@ldap_logger = true
|
15
|
+
|
10
16
|
# Add valid users to database
|
11
17
|
mattr_accessor :ldap_create_user
|
12
18
|
@@ldap_create_user = false
|
@@ -16,6 +22,12 @@ module Devise
|
|
16
22
|
|
17
23
|
mattr_accessor :ldap_update_password
|
18
24
|
@@ldap_update_password = true
|
25
|
+
|
26
|
+
mattr_accessor :ldap_check_group_membership
|
27
|
+
@@ldap_check_group_membership = false
|
28
|
+
|
29
|
+
mattr_accessor :ldap_check_attributes
|
30
|
+
@@ldap_check_role_attribute = false
|
19
31
|
end
|
20
32
|
|
21
33
|
# Add ldap_authenticatable strategy to defaults.
|
@@ -1,45 +1,135 @@
|
|
1
|
-
require
|
1
|
+
require "net/ldap"
|
2
2
|
|
3
3
|
module Devise
|
4
4
|
|
5
|
-
# simple adapter for ldap credential checking
|
6
|
-
# ::Devise.ldap_host
|
7
5
|
module LdapAdapter
|
8
6
|
|
9
7
|
def self.valid_credentials?(login, password_plaintext)
|
10
|
-
resource = LdapConnect.new
|
11
|
-
|
12
|
-
ldap.auth "#{resource.attribute}=#{login},#{ldap.base}", password_plaintext
|
13
|
-
ldap.bind # will return false if authentication is NOT successful
|
8
|
+
resource = LdapConnect.new(:login => login, :password => password_plaintext)
|
9
|
+
resource.authorized?
|
14
10
|
end
|
15
11
|
|
16
|
-
def self.update_password(login,
|
17
|
-
resource = LdapConnect.new
|
18
|
-
resource.
|
12
|
+
def self.update_password(login, new_password)
|
13
|
+
resource = LdapConnect.new(:login => login, :new_password => new_password)
|
14
|
+
resource.change_password! if new_password.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get_groups(login)
|
18
|
+
ldap = LdapConnect.new(:login => login)
|
19
|
+
ldap.user_groups
|
19
20
|
end
|
20
21
|
|
21
22
|
class LdapConnect
|
22
23
|
|
23
|
-
attr_reader :ldap, :base, :attribute
|
24
|
+
attr_reader :ldap, :login #, :base, :attribute, :required_groups, :login, :password, :new_password
|
24
25
|
|
25
26
|
def initialize(params = {})
|
26
27
|
ldap_config = YAML.load_file(::Devise.ldap_config || "#{Rails.root}/config/ldap.yml")[Rails.env]
|
27
|
-
ldap_options = params
|
28
28
|
ldap_options[:encryption] = :simple_tls if ldap_config["ssl"]
|
29
29
|
|
30
|
-
@ldap = Net::LDAP.new(ldap_options)
|
30
|
+
@ldap = Net::LDAP.new # (ldap_options)
|
31
31
|
@ldap.host = ldap_config["host"]
|
32
32
|
@ldap.port = ldap_config["port"]
|
33
33
|
@ldap.base = ldap_config["base"]
|
34
34
|
@attribute = ldap_config["attribute"]
|
35
|
+
|
36
|
+
@group_base = ldap_config["group_base"]
|
37
|
+
@required_groups = ldap_config["required_groups"]
|
38
|
+
@required_attributes = ldap_config["require_attribute"]
|
39
|
+
|
35
40
|
@ldap.auth ldap_config["admin_user"], ldap_config["admin_password"] if params[:admin]
|
41
|
+
|
42
|
+
@login = params[:login]
|
43
|
+
@password = params[:password]
|
44
|
+
@new_password = params[:new_password]
|
36
45
|
end
|
37
46
|
|
38
|
-
def dn
|
39
|
-
"#{@attribute}=#{login},#{@ldap.base}"
|
47
|
+
def dn
|
48
|
+
"#{@attribute}=#{@login},#{@ldap.base}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def authenticate!
|
52
|
+
@ldap.auth(dn, @password)
|
53
|
+
@ldap.bind
|
54
|
+
end
|
55
|
+
|
56
|
+
def authenticated?
|
57
|
+
authenticate!
|
58
|
+
end
|
59
|
+
|
60
|
+
def authorized?
|
61
|
+
DeviseLdapAuthenticatable::Logger.send("Authorizing user #{dn}")
|
62
|
+
authenticated? && in_required_groups? && has_required_attribute?
|
63
|
+
end
|
64
|
+
|
65
|
+
def change_password!
|
66
|
+
update_ldap(:userpassword => Net::LDAP::Password.generate(:sha, @new_password))
|
40
67
|
end
|
41
68
|
|
42
|
-
def
|
69
|
+
def in_required_groups?
|
70
|
+
return true unless ::Devise.ldap_check_group_membership
|
71
|
+
|
72
|
+
## FIXME set errors here, the ldap.yml isn't set properly.
|
73
|
+
return false if @required_groups.nil?
|
74
|
+
|
75
|
+
admin_ldap = LdapConnect.admin
|
76
|
+
|
77
|
+
for group in @required_groups
|
78
|
+
admin_ldap.search(:base => group, :scope => Net::LDAP::SearchScope_BaseObject) do |entry|
|
79
|
+
unless entry.uniqueMember.include? dn
|
80
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} is not in group: #{group}")
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_required_attribute?
|
90
|
+
return true unless ::Devise.ldap_check_attributes
|
91
|
+
|
92
|
+
admin_ldap = LdapConnect.admin
|
93
|
+
|
94
|
+
user = find_ldap_user(admin_ldap)
|
95
|
+
|
96
|
+
@required_attributes.each do |key,val|
|
97
|
+
unless user[key].include? val
|
98
|
+
DeviseLdapAuthenticatable::Logger.send("User #{dn} did not match attribute #{key}:#{val}")
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
return true
|
104
|
+
end
|
105
|
+
|
106
|
+
def user_groups
|
107
|
+
admin_ldap = LdapConnect.admin
|
108
|
+
|
109
|
+
DeviseLdapAuthenticatable::Logger.send("Getting groups for #{dn}")
|
110
|
+
filter = Net::LDAP::Filter.eq("uniqueMember", dn)
|
111
|
+
admin_ldap.search(:filter => filter, :base => @group_base).collect(&:dn)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def self.admin
|
117
|
+
ldap = LdapConnect.new(:admin => true).ldap
|
118
|
+
|
119
|
+
unless ldap.bind
|
120
|
+
DeviseLdapAuthenticatable::Logger.send("Cannot bind to admin LDAP user")
|
121
|
+
raise DeviseLdapAuthenticatable::LdapException, "Cannot connect to admin LDAP user"
|
122
|
+
end
|
123
|
+
|
124
|
+
return ldap
|
125
|
+
end
|
126
|
+
|
127
|
+
def find_ldap_user(ldap)
|
128
|
+
DeviseLdapAuthenticatable::Logger.send("Finding user: #{dn}")
|
129
|
+
ldap.search(:base => dn, :scope => Net::LDAP::SearchScope_BaseObject).try(:first)
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_ldap(ops)
|
43
133
|
operations = []
|
44
134
|
if ops.is_a? Hash
|
45
135
|
ops.each do |key,value|
|
@@ -49,21 +139,11 @@ module Devise
|
|
49
139
|
operations = ops
|
50
140
|
end
|
51
141
|
|
52
|
-
|
53
|
-
ldap.bind
|
142
|
+
admin_ldap = LdapConnect.admin
|
54
143
|
|
55
|
-
|
144
|
+
DeviseLdapAuthenticatable::Logger.send("Modifying user #{dn}")
|
145
|
+
admin_ldap.modify(:dn => dn, :operations => operations)
|
56
146
|
end
|
57
|
-
|
58
|
-
# ## This is for testing, It will clear all users out of the LDAP database. Useful to put in before hooks in rspec, cucumber, etc..
|
59
|
-
# def clear_users!(base = @ldap.base)
|
60
|
-
# raise "You should ONLY do this on the test enviornment! It will clear out all of the users in the LDAP server" if Rails.env != "test"
|
61
|
-
# if @ldap.bind
|
62
|
-
# @ldap.search(:filter => "#{@attribute}=*", :base => base) do |entry|
|
63
|
-
# @ldap.delete(:dn => entry.dn)
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
# end
|
67
147
|
|
68
148
|
end
|
69
149
|
|
@@ -25,20 +25,28 @@ module Devise
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
## FIXME find out how to get rid of this.
|
28
29
|
def clean_up_passwords
|
29
|
-
# self.password = nil
|
30
30
|
end
|
31
31
|
|
32
32
|
# Checks if a resource is valid upon authentication.
|
33
33
|
def valid_ldap_authentication?(password)
|
34
|
-
Devise::LdapAdapter.valid_credentials?(self.email, password)
|
34
|
+
if Devise::LdapAdapter.valid_credentials?(self.email, password)
|
35
|
+
return true
|
36
|
+
else
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def ldap_groups
|
42
|
+
Devise::LdapAdapter.get_groups(self.email)
|
35
43
|
end
|
36
44
|
|
37
45
|
module ClassMethods
|
38
46
|
# Authenticate a user based on configured attribute keys. Returns the
|
39
47
|
# authenticated user if it's valid or nil.
|
40
|
-
def authenticate_with_ldap(attributes={})
|
41
|
-
return unless attributes[:email].present?
|
48
|
+
def authenticate_with_ldap(attributes={})
|
49
|
+
return nil unless attributes[:email].present?
|
42
50
|
conditions = attributes.slice(:email)
|
43
51
|
|
44
52
|
unless conditions[:email]
|
@@ -52,9 +60,10 @@ module Devise
|
|
52
60
|
end
|
53
61
|
|
54
62
|
if resource.try(:valid_ldap_authentication?, attributes[:password])
|
55
|
-
resource.
|
63
|
+
resource.save if resource.new_record?
|
64
|
+
return resource
|
56
65
|
else
|
57
|
-
nil
|
66
|
+
return nil
|
58
67
|
end
|
59
68
|
|
60
69
|
end
|
@@ -4,6 +4,8 @@ module DeviseLdapAuthenticatable
|
|
4
4
|
|
5
5
|
class_option :user_model, :type => :string, :default => "user", :desc => "Model to update"
|
6
6
|
class_option :update_model, :type => :boolean, :default => true, :desc => "Update model to change from database_authenticatable to ldap_authenticatable"
|
7
|
+
class_option :add_rescue, :type => :boolean, :default => true, :desc => "Update Application Controller with resuce_from for DeviseLdapAuthenticatable::LdapException"
|
8
|
+
|
7
9
|
|
8
10
|
def create_ldap_config
|
9
11
|
copy_file "ldap.yml", "config/ldap.yml"
|
@@ -17,15 +19,29 @@ module DeviseLdapAuthenticatable
|
|
17
19
|
gsub_file "app/models/#{options.user_model}.rb", /:database_authenticatable/, ":ldap_authenticatable" if options.update_model?
|
18
20
|
end
|
19
21
|
|
22
|
+
def update_application_controller
|
23
|
+
inject_into_class "app/controllers/application_controller.rb", ApplicationController, rescue_from_exception if options.add_rescue?
|
24
|
+
end
|
25
|
+
|
20
26
|
private
|
21
27
|
|
22
28
|
def default_devise_settings
|
23
29
|
<<-eof
|
24
30
|
# ==> LDAP Configuration
|
31
|
+
# config.ldap_logger = true
|
25
32
|
# config.ldap_create_user = false
|
26
33
|
# config.ldap_update_password = true
|
27
34
|
# config.ldap_config = "\#{Rails.root}/config/ldap.yml"
|
35
|
+
# config.ldap_check_group_membership = false
|
36
|
+
# config.ldap_check_attributes = false
|
37
|
+
eof
|
38
|
+
end
|
28
39
|
|
40
|
+
def rescue_from_exception
|
41
|
+
<<-eof
|
42
|
+
rescue_from DeviseLdapAuthenticatable::LdapException do |exception|
|
43
|
+
render :text => exception, :status => 500
|
44
|
+
end
|
29
45
|
eof
|
30
46
|
end
|
31
47
|
|
@@ -1,27 +1,48 @@
|
|
1
|
+
## Authorizations
|
2
|
+
# Uncomment out the merging for each enviornment that you'd like to include.
|
3
|
+
# You can also just copy and paste the tree (do not include the "authorizations") to each
|
4
|
+
# enviornment if you need something different per enviornment.
|
5
|
+
authorizations: &AUTHORIZATIONS
|
6
|
+
group_base: ou=groups,dc=test,dc=com
|
7
|
+
## Requires config.ldap_check_group_membership in devise.rb be true
|
8
|
+
# Can have multiple values, must match all to be authorized
|
9
|
+
required_groups:
|
10
|
+
- cn=admins,ou=groups,dc=test,dc=com
|
11
|
+
- cn=users,ou=groups,dc=test,dc=com
|
12
|
+
## Requires config.ldap_check_attributes in devise.rb to be true
|
13
|
+
## Can have multiple attributes and values, must match all to be authorized
|
14
|
+
require_attribute:
|
15
|
+
objectClass: inetOrgPerson
|
16
|
+
authorizationRole: postsAdmin
|
17
|
+
|
18
|
+
## Enviornments
|
19
|
+
|
1
20
|
development:
|
2
21
|
host: localhost
|
3
22
|
port: 389
|
4
23
|
attribute: cn
|
5
|
-
base: ou=people,dc=
|
6
|
-
admin_user: cn=admin,dc=
|
24
|
+
base: ou=people,dc=test,dc=com
|
25
|
+
admin_user: cn=admin,dc=test,dc=com
|
7
26
|
admin_password: admin_password
|
8
27
|
ssl: false
|
28
|
+
# <<: *AUTHORIZATIONS
|
9
29
|
|
10
30
|
test:
|
11
31
|
host: localhost
|
12
32
|
port: 3389
|
13
33
|
attribute: cn
|
14
|
-
base: ou=people,dc=
|
15
|
-
admin_user: cn=admin,dc=
|
34
|
+
base: ou=people,dc=test,dc=com
|
35
|
+
admin_user: cn=admin,dc=test,dc=com
|
16
36
|
admin_password: admin_password
|
17
37
|
ssl: false
|
38
|
+
# <<: *AUTHORIZATIONS
|
18
39
|
|
19
40
|
production:
|
20
41
|
host: localhost
|
21
42
|
port: 636
|
22
43
|
attribute: cn
|
23
|
-
base: ou=people,dc=
|
24
|
-
admin_user: cn=admin,dc=
|
44
|
+
base: ou=people,dc=test,dc=com
|
45
|
+
admin_user: cn=admin,dc=test,dc=com
|
25
46
|
admin_password: admin_password
|
26
47
|
ssl: true
|
27
|
-
|
48
|
+
# <<: *AUTHORIZATIONS
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Daniel McNevin
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-07-
|
18
|
+
date: 2010-07-18 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
37
|
+
name: net-ldap
|
38
38
|
prerelease: false
|
39
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
@@ -43,15 +43,14 @@ dependencies:
|
|
43
43
|
- !ruby/object:Gem::Version
|
44
44
|
segments:
|
45
45
|
- 0
|
46
|
-
-
|
47
|
-
-
|
48
|
-
version: 0.
|
46
|
+
- 1
|
47
|
+
- 1
|
48
|
+
version: 0.1.1
|
49
49
|
type: :runtime
|
50
50
|
version_requirements: *id002
|
51
51
|
description: LDAP Authentication for Devise
|
52
52
|
email:
|
53
53
|
- dpmcnevin@gmail.com
|
54
|
-
- curtis.schiewek@gmail.com
|
55
54
|
executables: []
|
56
55
|
|
57
56
|
extensions: []
|
@@ -59,7 +58,9 @@ extensions: []
|
|
59
58
|
extra_rdoc_files: []
|
60
59
|
|
61
60
|
files:
|
61
|
+
- lib/devise_ldap_authenticatable/exception.rb
|
62
62
|
- lib/devise_ldap_authenticatable/ldap_adapter.rb
|
63
|
+
- lib/devise_ldap_authenticatable/logger.rb
|
63
64
|
- lib/devise_ldap_authenticatable/model.rb
|
64
65
|
- lib/devise_ldap_authenticatable/routes.rb
|
65
66
|
- lib/devise_ldap_authenticatable/schema.rb
|