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 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
- - ruby-net-ldap 0.0.4
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.3.5"
29
-
30
- For latest version, use the git repository instead
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\_create\_user
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.
@@ -0,0 +1,6 @@
1
+ module DeviseLdapAuthenticatable
2
+
3
+ class LdapException < Exception
4
+ end
5
+
6
+ end
@@ -1,45 +1,135 @@
1
- require 'net/ldap'
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
- ldap = resource.ldap
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, plaintext_password)
17
- resource = LdapConnect.new
18
- resource.update_ldap(login, :userpassword => Net::LDAP::Password.generate(:sha, plaintext_password)) if plaintext_password.present?
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(login)
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 update_ldap(login,ops)
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
- ldap = LdapConnect.new(:admin => true).ldap
53
- ldap.bind
142
+ admin_ldap = LdapConnect.admin
54
143
 
55
- ldap.modify(:dn => dn(login), :operations => operations)
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
 
@@ -0,0 +1,11 @@
1
+ module DeviseLdapAuthenticatable
2
+
3
+ class Logger
4
+ def self.send(message, logger = Rails.logger)
5
+ if ::Devise.ldap_logger
6
+ logger.add 0, " \e[36mLDAP:\e[0m #{message}"
7
+ end
8
+ end
9
+ end
10
+
11
+ end
@@ -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.new_record? ? resource.save : 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
@@ -1,4 +1,4 @@
1
1
  module DeviseLdapAuthenticatable
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0"
3
3
  end
4
4
 
@@ -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=yourdomain,dc=com
6
- admin_user: cn=admin,dc=yourdomain,dc=com
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=yourdomain,dc=com
15
- admin_user: cn=admin,dc=yourdomain,dc=com
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=yourdomain,dc=com
24
- admin_user: cn=admin,dc=yourdomain,dc=com
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
- - 3
8
- - 5
9
- version: 0.3.5
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-13 00:00:00 -04:00
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: ruby-net-ldap
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
- - 0
47
- - 4
48
- version: 0.0.4
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