charanya-devise_ldap_authenticatable 0.4.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.
Files changed (94) hide show
  1. data/.gitignore +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +175 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/charanya-devise_ldap_authenticatable.gemspec +173 -0
  7. data/lib/devise_ldap_authenticatable.rb +45 -0
  8. data/lib/devise_ldap_authenticatable/exception.rb +6 -0
  9. data/lib/devise_ldap_authenticatable/ldap_adapter.rb +237 -0
  10. data/lib/devise_ldap_authenticatable/logger.rb +11 -0
  11. data/lib/devise_ldap_authenticatable/model.rb +118 -0
  12. data/lib/devise_ldap_authenticatable/routes.rb +8 -0
  13. data/lib/devise_ldap_authenticatable/schema.rb +14 -0
  14. data/lib/devise_ldap_authenticatable/strategy.rb +36 -0
  15. data/lib/devise_ldap_authenticatable/version.rb +4 -0
  16. data/lib/generators/devise_ldap_authenticatable/install_generator.rb +61 -0
  17. data/lib/generators/devise_ldap_authenticatable/templates/ldap.yml +75 -0
  18. data/rails/init.rb +2 -0
  19. data/test/devise_ldap_authenticatable_test.rb +8 -0
  20. data/test/ldap/base.ldif +73 -0
  21. data/test/ldap/clear.ldif +26 -0
  22. data/test/ldap/local.schema +6 -0
  23. data/test/ldap/run-server.sh +10 -0
  24. data/test/ldap/server.pem +38 -0
  25. data/test/ldap/slapd-ssl-test.conf +107 -0
  26. data/test/ldap/slapd-test.conf +107 -0
  27. data/test/rails_app/Gemfile +22 -0
  28. data/test/rails_app/Gemfile.lock +157 -0
  29. data/test/rails_app/Rakefile +7 -0
  30. data/test/rails_app/app/controllers/application_controller.rb +4 -0
  31. data/test/rails_app/app/controllers/posts_controller.rb +15 -0
  32. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  33. data/test/rails_app/app/helpers/posts_helper.rb +2 -0
  34. data/test/rails_app/app/models/post.rb +2 -0
  35. data/test/rails_app/app/models/user.rb +10 -0
  36. data/test/rails_app/app/views/layouts/application.html.erb +26 -0
  37. data/test/rails_app/app/views/posts/index.html.erb +2 -0
  38. data/test/rails_app/config.ru +4 -0
  39. data/test/rails_app/config/application.rb +46 -0
  40. data/test/rails_app/config/boot.rb +13 -0
  41. data/test/rails_app/config/cucumber.yml +8 -0
  42. data/test/rails_app/config/database.yml +25 -0
  43. data/test/rails_app/config/environment.rb +5 -0
  44. data/test/rails_app/config/environments/development.rb +22 -0
  45. data/test/rails_app/config/environments/production.rb +46 -0
  46. data/test/rails_app/config/environments/test.rb +34 -0
  47. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  48. data/test/rails_app/config/initializers/devise.rb +140 -0
  49. data/test/rails_app/config/initializers/inflections.rb +10 -0
  50. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  51. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  52. data/test/rails_app/config/initializers/session_store.rb +8 -0
  53. data/test/rails_app/config/ldap.yml +22 -0
  54. data/test/rails_app/config/ldap_with_erb.yml +23 -0
  55. data/test/rails_app/config/ldap_with_uid.yml +18 -0
  56. data/test/rails_app/config/locales/devise.en.yml +39 -0
  57. data/test/rails_app/config/locales/en.yml +5 -0
  58. data/test/rails_app/config/routes.rb +64 -0
  59. data/test/rails_app/config/ssl_ldap.yml +21 -0
  60. data/test/rails_app/config/ssl_ldap_with_erb.yml +23 -0
  61. data/test/rails_app/config/ssl_ldap_with_uid.yml +18 -0
  62. data/test/rails_app/db/migrate/20100708120302_create_posts.rb +14 -0
  63. data/test/rails_app/db/migrate/20100708120448_devise_create_users.rb +26 -0
  64. data/test/rails_app/db/schema.rb +42 -0
  65. data/test/rails_app/db/seeds.rb +7 -0
  66. data/test/rails_app/features/manage_logins.feature +35 -0
  67. data/test/rails_app/features/step_definitions/login_steps.rb +21 -0
  68. data/test/rails_app/features/step_definitions/web_steps.rb +219 -0
  69. data/test/rails_app/features/support/env.rb +58 -0
  70. data/test/rails_app/features/support/paths.rb +38 -0
  71. data/test/rails_app/lib/tasks/.gitkeep +0 -0
  72. data/test/rails_app/lib/tasks/cucumber.rake +53 -0
  73. data/test/rails_app/public/404.html +26 -0
  74. data/test/rails_app/public/422.html +26 -0
  75. data/test/rails_app/public/500.html +26 -0
  76. data/test/rails_app/public/images/rails.png +0 -0
  77. data/test/rails_app/public/javascripts/application.js +2 -0
  78. data/test/rails_app/public/javascripts/controls.js +965 -0
  79. data/test/rails_app/public/javascripts/dragdrop.js +974 -0
  80. data/test/rails_app/public/javascripts/effects.js +1123 -0
  81. data/test/rails_app/public/javascripts/prototype.js +4874 -0
  82. data/test/rails_app/public/javascripts/rails.js +118 -0
  83. data/test/rails_app/public/stylesheets/.gitkeep +0 -0
  84. data/test/rails_app/script/cucumber +10 -0
  85. data/test/rails_app/script/rails +6 -0
  86. data/test/rails_app/test/factories/users.rb +14 -0
  87. data/test/rails_app/test/functional/posts_controller_test.rb +58 -0
  88. data/test/rails_app/test/performance/browsing_test.rb +9 -0
  89. data/test/rails_app/test/test_helper.rb +36 -0
  90. data/test/rails_app/test/unit/helpers/posts_helper_test.rb +4 -0
  91. data/test/rails_app/test/unit/post_test.rb +4 -0
  92. data/test/rails_app/test/unit/user_test.rb +211 -0
  93. data/test/test_helper.rb +3 -0
  94. metadata +217 -0
@@ -0,0 +1,6 @@
1
+ module DeviseLdapAuthenticatable
2
+
3
+ class LdapException < Exception
4
+ end
5
+
6
+ end
@@ -0,0 +1,237 @@
1
+ require "net/ldap"
2
+
3
+ module Devise
4
+
5
+ module LdapAdapter
6
+
7
+ def self.valid_credentials?(login, password_plaintext)
8
+ options = {:login => login,
9
+ :password => password_plaintext,
10
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
11
+ :admin => ::Devise.ldap_use_admin_to_bind}
12
+
13
+ resource = LdapConnect.new(options)
14
+ resource.authorized?
15
+ end
16
+
17
+ def self.update_password(login, new_password)
18
+ options = {:login => login,
19
+ :new_password => new_password,
20
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
21
+ :admin => ::Devise.ldap_use_admin_to_bind}
22
+
23
+ resource = LdapConnect.new(options)
24
+ resource.change_password! if new_password.present?
25
+ end
26
+
27
+ def self.get_groups(login)
28
+ options = {:login => login,
29
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
30
+ :admin => ::Devise.ldap_use_admin_to_bind}
31
+
32
+ ldap = LdapConnect.new(options)
33
+ ldap.user_groups
34
+ end
35
+
36
+ def self.get_dn(login)
37
+ options = {:login => login,
38
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
39
+ :admin => ::Devise.ldap_use_admin_to_bind}
40
+ resource = LdapConnect.new(options)
41
+ resource.dn
42
+ end
43
+
44
+ # Get the values of the attributes to be synched on to the Applications db.
45
+ # Application Specific Change
46
+
47
+ def self.get_persistant_attributes(login)
48
+ options = {:login => login,
49
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
50
+ :admin => ::Devise.ldap_use_admin_to_bind}
51
+ resource = LdapConnect.new(options)
52
+ resource.persistant_attributes
53
+ end
54
+
55
+ # Get the value to be compared with the db field for authenticating.
56
+ # Application Specific Change
57
+
58
+ def self.get_attribute_to_compare(login)
59
+ options = {:login => login,
60
+ :ldap_auth_username_builder => ::Devise.ldap_auth_username_builder,
61
+ :admin => ::Devise.ldap_use_admin_to_bind}
62
+ resource = LdapConnect.new(options)
63
+ resource.attribute_to_compare
64
+ end
65
+
66
+ class LdapConnect
67
+
68
+ attr_reader :ldap, :login
69
+
70
+ def initialize(params = {})
71
+
72
+ ldap_config = YAML.load(ERB.new(File.read(::Devise.ldap_config || "#{Rails.root}/config/ldap.yml")).result)[Rails.env]
73
+ ldap_options = params
74
+ ldap_options[:encryption] = :simple_tls if ldap_config["ssl"]
75
+
76
+ @ldap = Net::LDAP.new(ldap_options)
77
+ @ldap.host = ldap_config["host"]
78
+ @ldap.port = ldap_config["port"]
79
+ @ldap.base = ldap_config["base"]
80
+ @attribute = ldap_config["attribute"]
81
+ # Application Specific Change
82
+ @attribute_to_be_compared = ldap_config["attribute_to_compare"]
83
+ @attributes_to_persist = ldap_config["attributes_to_persist"]
84
+
85
+ @ldap_auth_username_builder = params[:ldap_auth_username_builder]
86
+ @group_base = ldap_config["group_base"]
87
+ @required_groups = ldap_config["required_groups"]
88
+ @required_attributes = ldap_config["require_attribute"]
89
+
90
+ @ldap.auth ldap_config["admin_user"], ldap_config["admin_password"] if params[:admin]
91
+
92
+ @login = params[:login]
93
+ @password = params[:password]
94
+ @new_password = params[:new_password]
95
+ end
96
+
97
+ # Application Specific Change
98
+ def persistant_attributes
99
+
100
+ user_entry = find_ldap_user(ldap)
101
+ attribute_hash = {}
102
+
103
+ @attributes_to_persist.each do |attr|
104
+ attribute_hash[attr[1]] = user_entry[attr[0]]
105
+ end
106
+ attribute_hash
107
+ end
108
+
109
+ # Application Specific Change
110
+ def attribute_to_compare
111
+ user_entry = find_ldap_user(ldap)
112
+ user_entry[@attribute_to_be_compared]
113
+ end
114
+
115
+ def dn
116
+ DeviseLdapAuthenticatable::Logger.send("LDAP search: #{@attribute}=#{@login}")
117
+ filter = Net::LDAP::Filter.eq(@attribute.to_s, @login.to_s)
118
+ ldap_entry = nil
119
+ @ldap.search(:filter => filter) {|entry| ldap_entry = entry}
120
+ if ldap_entry.nil?
121
+ @ldap_auth_username_builder.call(@attribute,@login,@ldap)
122
+ else
123
+ ldap_entry.dn
124
+ end
125
+ end
126
+
127
+ def authenticate!
128
+ @ldap.auth(dn, @password)
129
+ @ldap.bind
130
+ end
131
+
132
+ def authenticated?
133
+ authenticate!
134
+ end
135
+
136
+ def authorized?
137
+ DeviseLdapAuthenticatable::Logger.send("Authorizing user #{dn}")
138
+ authenticated? && in_required_groups? && has_required_attribute?
139
+ end
140
+
141
+ def change_password!
142
+ update_ldap(:userpassword => Net::LDAP::Password.generate(:sha, @new_password))
143
+ end
144
+
145
+ def in_required_groups?
146
+ return true unless ::Devise.ldap_check_group_membership
147
+
148
+ ## FIXME set errors here, the ldap.yml isn't set properly.
149
+ DeviseLdapAuthenticatable::Logger.send("Required Groups are #{@required_groups}")
150
+ return false if @required_groups.nil?
151
+
152
+ # admin_ldap = LdapConnect.admin
153
+ # Admin bind is not really needed, but might depend on sepecific LDAP configuration.Knome Specific
154
+ admin_ldap = @ldap
155
+
156
+ for group in @required_groups
157
+ if group.is_a?(Array)
158
+ group_attribute, group_name = group
159
+ else
160
+ group_attribute = "uniqueMember"
161
+ group_name = group
162
+ end
163
+ admin_ldap.search(:base => group_name, :scope => Net::LDAP::SearchScope_BaseObject) do |entry|
164
+ unless entry[group_attribute].include? dn
165
+ DeviseLdapAuthenticatable::Logger.send("User #{dn} is not in group: #{group_name }")
166
+ return false
167
+ end
168
+ end
169
+ end
170
+
171
+ return true
172
+ end
173
+
174
+ def has_required_attribute?
175
+ return true unless ::Devise.ldap_check_attributes
176
+
177
+ admin_ldap = LdapConnect.admin
178
+
179
+ user = find_ldap_user(admin_ldap)
180
+
181
+ @required_attributes.each do |key,val|
182
+ unless user[key].include? val
183
+ DeviseLdapAuthenticatable::Logger.send("User #{dn} did not match attribute #{key}:#{val}")
184
+ return false
185
+ end
186
+ end
187
+
188
+ return true
189
+ end
190
+
191
+ def user_groups
192
+ admin_ldap = LdapConnect.admin
193
+
194
+ DeviseLdapAuthenticatable::Logger.send("Getting groups for #{dn}")
195
+ filter = Net::LDAP::Filter.eq("uniqueMember", dn)
196
+ admin_ldap.search(:filter => filter, :base => @group_base).collect(&:dn)
197
+ end
198
+
199
+ private
200
+
201
+ def self.admin
202
+ ldap = LdapConnect.new(:admin => true).ldap
203
+
204
+ unless ldap.bind
205
+ DeviseLdapAuthenticatable::Logger.send("Cannot bind to admin LDAP user")
206
+ raise DeviseLdapAuthenticatable::LdapException, "Cannot connect to admin LDAP user"
207
+ end
208
+
209
+ return ldap
210
+ end
211
+
212
+ def find_ldap_user(ldap)
213
+ DeviseLdapAuthenticatable::Logger.send("Finding user: #{dn}")
214
+ ldap.search(:base => dn, :scope => Net::LDAP::SearchScope_BaseObject).try(:first)
215
+ end
216
+
217
+ def update_ldap(ops)
218
+ operations = []
219
+ if ops.is_a? Hash
220
+ ops.each do |key,value|
221
+ operations << [:replace,key,value]
222
+ end
223
+ elsif ops.is_a? Array
224
+ operations = ops
225
+ end
226
+
227
+ admin_ldap = LdapConnect.admin
228
+
229
+ DeviseLdapAuthenticatable::Logger.send("Modifying user #{dn}")
230
+ admin_ldap.modify(:dn => dn, :operations => operations)
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+
237
+ end
@@ -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
@@ -0,0 +1,118 @@
1
+ require 'devise_ldap_authenticatable/strategy'
2
+
3
+ module Devise
4
+ module Models
5
+ # LDAP Module, responsible for validating the user credentials via LDAP.
6
+ #
7
+ # Examples:
8
+ #
9
+ # User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
10
+ # User.find(1).valid_password?('password123') # returns true/false
11
+ #
12
+ module LdapAuthenticatable
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ attr_reader :current_password, :password
17
+ attr_accessor :password_confirmation
18
+ end
19
+
20
+ def login_with
21
+ self[::Devise.authentication_keys.first]
22
+ end
23
+
24
+ def reset_password!(new_password, new_password_confirmation)
25
+ if new_password == new_password_confirmation && ::Devise.ldap_update_password
26
+ Devise::LdapAdapter.update_password(login_with, new_password)
27
+ end
28
+ clear_reset_password_token if valid?
29
+ save
30
+ end
31
+
32
+ def password=(new_password)
33
+ @password = new_password
34
+ end
35
+
36
+ # Checks if a resource is valid upon authentication.
37
+ def valid_ldap_authentication?(password)
38
+ if Devise::LdapAdapter.valid_credentials?(login_with, password)
39
+ return true
40
+ else
41
+ return false
42
+ end
43
+ end
44
+
45
+ def ldap_groups
46
+ Devise::LdapAdapter.get_groups(login_with)
47
+ end
48
+
49
+ def ldap_dn
50
+ Devise::LdapAdapter.get_dn(login_with)
51
+ end
52
+
53
+ #Adding functions for synchronizing the values(user specified) between LDAP Metadata and Application database entry.
54
+ # Application Specific Change
55
+ def ldap_persistant_attributes
56
+ Devise::LdapAdapter.get_persistant_attributes(login_with)
57
+ end
58
+
59
+ def update_persistant_attributes
60
+ ldap_persistant_attributes.each do |key,value|
61
+ self[key.to_sym] = value.to_s
62
+ end
63
+ end
64
+
65
+ # Adding this to get the field in db table with which the provided login detail should be compared to authenticate.
66
+ # Application Specific Change
67
+ def attribute_to_compare
68
+ Devise::LdapAdapter.get_attribute_to_compare(login_with)
69
+ end
70
+
71
+
72
+ module ClassMethods
73
+ # Authenticate a user based on configured attribute keys. Returns the
74
+ # authenticated user if it's valid or nil.
75
+ def authenticate_with_ldap(attributes={})
76
+
77
+ @login_with = ::Devise.authentication_keys.first
78
+ return nil unless attributes[@login_with].present?
79
+
80
+ # Get the value to be compared within the database against the @login_with field
81
+ # According to User Specified details
82
+ # Application Specific Change
83
+
84
+ attribute_to_login_with = attributes[@login_with]
85
+ resource = new
86
+ resource[@login_with] = attributes[@login_with]
87
+ resource.password = attributes[:password]
88
+
89
+ if resource.try(:valid_ldap_authentication?, attributes[:password])
90
+ attribute_to_login_with = resource.attribute_to_compare
91
+ resource = where(@login_with => attribute_to_login_with).first
92
+
93
+ if (resource.blank? and ::Devise.ldap_create_user)
94
+ resource = new
95
+ resource[@login_with] = attributes[@login_with]
96
+ resource.password = attributes[:password]
97
+ end
98
+ unless resource.blank?
99
+ resource[@login_with] = attributes[@login_with]
100
+ resource.update_persistant_attributes
101
+ DeviseLdapAuthenticatable::Logger.send("Updating persistant attributes") unless resource.new_record?
102
+ resource.save
103
+ end
104
+ return resource
105
+ else
106
+ return nil
107
+ end
108
+
109
+ end
110
+
111
+ def update_with_password(resource)
112
+ puts "UPDATE_WITH_PASSWORD: #{resource.inspect}"
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,8 @@
1
+ ## No routes needed anymore since Devise.add_module with the :route parameter will take care of it.
2
+
3
+ # ActionController::Routing::RouteSet::Mapper.class_eval do
4
+ #
5
+ # protected
6
+ # # reuse the session routes and controller
7
+ # alias :ldap_authenticatable :database_authenticatable
8
+ # end
@@ -0,0 +1,14 @@
1
+ ## Using email now instead of login. Will add an option later on.
2
+
3
+ # Devise::Schema.class_eval do
4
+ # # Creates login
5
+ # #
6
+ # # == Options
7
+ # # * :null - When true, allow columns to be null.
8
+ # def ldap_authenticatable(options={})
9
+ # null = options[:null] || false
10
+ #
11
+ # apply_schema :login, String, :null => null
12
+ # end
13
+ #
14
+ # end
@@ -0,0 +1,36 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Strategy for signing in a user based on his login and password using LDAP.
6
+ # Redirects to sign_in page if it's not authenticated
7
+ class LdapAuthenticatable < Authenticatable
8
+ def valid?
9
+ valid_controller? && valid_params? && mapping.to.respond_to?(:authenticate_with_ldap)
10
+ end
11
+
12
+ # Authenticate a user based on login and password params, returning to warden
13
+ # success and the authenticated user if everything is okay. Otherwise redirect
14
+ # to sign in page.
15
+ def authenticate!
16
+ if resource = mapping.to.authenticate_with_ldap(params[scope])
17
+ success!(resource)
18
+ else
19
+ fail(:invalid)
20
+ end
21
+ end
22
+
23
+ protected
24
+
25
+ def valid_controller?
26
+ params[:controller] == mapping.controllers[:sessions]
27
+ end
28
+
29
+ def valid_params?
30
+ params[scope] && params[scope][:password].present?
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)
@@ -0,0 +1,4 @@
1
+ module DeviseLdapAuthenticatable
2
+ VERSION = "0.4.6"
3
+ end
4
+
@@ -0,0 +1,61 @@
1
+ module DeviseLdapAuthenticatable
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("../templates", __FILE__)
4
+
5
+ class_option :user_model, :type => :string, :default => "user", :desc => "Model to update"
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
+ class_option :advanced, :type => :boolean, :desc => "Add advanced config options to the devise initializer"
9
+
10
+
11
+ def create_ldap_config
12
+ copy_file "ldap.yml", "config/ldap.yml"
13
+ end
14
+
15
+ def create_default_devise_settings
16
+ inject_into_file "config/initializers/devise.rb", default_devise_settings, :after => "Devise.setup do |config|\n"
17
+ end
18
+
19
+ def update_user_model
20
+ gsub_file "app/models/#{options.user_model}.rb", /:database_authenticatable/, ":ldap_authenticatable" if options.update_model?
21
+ end
22
+
23
+ def update_application_controller
24
+ inject_into_class "app/controllers/application_controller.rb", ApplicationController, rescue_from_exception if options.add_rescue?
25
+ end
26
+
27
+ private
28
+
29
+ def default_devise_settings
30
+ settings = <<-eof
31
+ # ==> LDAP Configuration
32
+ # config.ldap_logger = true
33
+ # config.ldap_create_user = false
34
+ # config.ldap_update_password = true
35
+ # config.ldap_config = "\#{Rails.root}/config/ldap.yml"
36
+ # config.ldap_check_group_membership = false
37
+ # config.ldap_check_attributes = false
38
+ # config.ldap_use_admin_to_bind = false
39
+
40
+ eof
41
+ if options.advanced?
42
+ settings << <<-eof
43
+ # ==> Advanced LDAP Configuration
44
+ # config.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "\#{attribute}=\#{login},\#{ldap.base}" }
45
+
46
+ eof
47
+ end
48
+
49
+ settings
50
+ end
51
+
52
+ def rescue_from_exception
53
+ <<-eof
54
+ rescue_from DeviseLdapAuthenticatable::LdapException do |exception|
55
+ render :text => exception, :status => 500
56
+ end
57
+ eof
58
+ end
59
+
60
+ end
61
+ end