devise_ldap_authenticatable 0.3.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|