xn_devise_ldap_authenticatable 0.8.5

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 (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +8 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +143 -0
  7. data/Rakefile +16 -0
  8. data/lib/devise_ldap_authenticatable.rb +49 -0
  9. data/lib/devise_ldap_authenticatable/exception.rb +6 -0
  10. data/lib/devise_ldap_authenticatable/ldap/adapter.rb +87 -0
  11. data/lib/devise_ldap_authenticatable/ldap/connection.rb +243 -0
  12. data/lib/devise_ldap_authenticatable/logger.rb +11 -0
  13. data/lib/devise_ldap_authenticatable/model.rb +120 -0
  14. data/lib/devise_ldap_authenticatable/strategy.rb +39 -0
  15. data/lib/devise_ldap_authenticatable/version.rb +3 -0
  16. data/lib/generators/devise_ldap_authenticatable/install_generator.rb +63 -0
  17. data/lib/generators/devise_ldap_authenticatable/templates/ldap.yml +55 -0
  18. data/spec/ldap/.gitignore +2 -0
  19. data/spec/ldap/base.ldif +73 -0
  20. data/spec/ldap/clear.ldif +26 -0
  21. data/spec/ldap/local.schema +6 -0
  22. data/spec/ldap/openldap-data/.gitignore +2 -0
  23. data/spec/ldap/openldap-data/run/.gitignore +2 -0
  24. data/spec/ldap/openldap-data/run/.gitkeep +0 -0
  25. data/spec/ldap/run-server +31 -0
  26. data/spec/ldap/server.pem +38 -0
  27. data/spec/ldap/slapd-test.conf.erb +107 -0
  28. data/spec/rails_app/Rakefile +7 -0
  29. data/spec/rails_app/app/controllers/application_controller.rb +7 -0
  30. data/spec/rails_app/app/controllers/posts_controller.rb +15 -0
  31. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  32. data/spec/rails_app/app/helpers/posts_helper.rb +2 -0
  33. data/spec/rails_app/app/models/post.rb +2 -0
  34. data/spec/rails_app/app/models/user.rb +7 -0
  35. data/spec/rails_app/app/views/layouts/application.html.erb +26 -0
  36. data/spec/rails_app/app/views/posts/index.html.erb +2 -0
  37. data/spec/rails_app/config.ru +4 -0
  38. data/spec/rails_app/config/application.rb +46 -0
  39. data/spec/rails_app/config/boot.rb +13 -0
  40. data/spec/rails_app/config/cucumber.yml +8 -0
  41. data/spec/rails_app/config/database.yml +25 -0
  42. data/spec/rails_app/config/environment.rb +5 -0
  43. data/spec/rails_app/config/environments/development.rb +21 -0
  44. data/spec/rails_app/config/environments/production.rb +46 -0
  45. data/spec/rails_app/config/environments/test.rb +34 -0
  46. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  47. data/spec/rails_app/config/initializers/devise.rb +247 -0
  48. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  49. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  50. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  51. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  52. data/spec/rails_app/config/ldap.yml +22 -0
  53. data/spec/rails_app/config/ldap_with_boolean_ssl.yml +22 -0
  54. data/spec/rails_app/config/ldap_with_erb.yml +23 -0
  55. data/spec/rails_app/config/ldap_with_uid.yml +18 -0
  56. data/spec/rails_app/config/locales/devise.en.yml +59 -0
  57. data/spec/rails_app/config/locales/en.yml +5 -0
  58. data/spec/rails_app/config/routes.rb +64 -0
  59. data/spec/rails_app/config/ssl_ldap.yml +21 -0
  60. data/spec/rails_app/config/ssl_ldap_with_erb.yml +23 -0
  61. data/spec/rails_app/config/ssl_ldap_with_uid.yml +18 -0
  62. data/spec/rails_app/db/migrate/20100708120448_devise_create_users.rb +40 -0
  63. data/spec/rails_app/db/schema.rb +35 -0
  64. data/spec/rails_app/features/manage_logins.feature +35 -0
  65. data/spec/rails_app/features/step_definitions/login_steps.rb +21 -0
  66. data/spec/rails_app/features/step_definitions/web_steps.rb +219 -0
  67. data/spec/rails_app/features/support/env.rb +58 -0
  68. data/spec/rails_app/features/support/paths.rb +38 -0
  69. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  70. data/spec/rails_app/lib/tasks/cucumber.rake +53 -0
  71. data/spec/rails_app/public/404.html +26 -0
  72. data/spec/rails_app/public/422.html +26 -0
  73. data/spec/rails_app/public/500.html +26 -0
  74. data/spec/rails_app/public/images/rails.png +0 -0
  75. data/spec/rails_app/public/javascripts/application.js +2 -0
  76. data/spec/rails_app/public/javascripts/controls.js +965 -0
  77. data/spec/rails_app/public/javascripts/dragdrop.js +974 -0
  78. data/spec/rails_app/public/javascripts/effects.js +1123 -0
  79. data/spec/rails_app/public/javascripts/prototype.js +4874 -0
  80. data/spec/rails_app/public/javascripts/rails.js +118 -0
  81. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  82. data/spec/rails_app/script/cucumber +10 -0
  83. data/spec/rails_app/script/rails +6 -0
  84. data/spec/spec_helper.rb +55 -0
  85. data/spec/support/factories.rb +16 -0
  86. data/spec/unit/connection_spec.rb +14 -0
  87. data/spec/unit/user_spec.rb +331 -0
  88. data/xn_devise_ldap_authenticatable.gemspec +36 -0
  89. metadata +363 -0
@@ -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,120 @@
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
+ @login_with ||= Devise.mappings.find {|k,v| v.class_name == self.class.name}.last.to.authentication_keys.first
22
+ self[@login_with]
23
+ end
24
+
25
+ def change_password!(current_password)
26
+ raise "Need to set new password first" if @password.blank?
27
+
28
+ Devise::LDAP::Adapter.update_own_password(login_with, @password, current_password)
29
+ end
30
+
31
+ def reset_password!(new_password, new_password_confirmation)
32
+ if new_password == new_password_confirmation && ::Devise.ldap_update_password
33
+ Devise::LDAP::Adapter.update_password(login_with, new_password)
34
+ end
35
+ clear_reset_password_token if valid?
36
+ save
37
+ end
38
+
39
+ def password=(new_password)
40
+ @password = new_password
41
+ if defined?(password_digest) && @password.present? && respond_to?(:encrypted_password=)
42
+ self.encrypted_password = password_digest(@password)
43
+ end
44
+ end
45
+
46
+ # Checks if a resource is valid upon authentication.
47
+ def valid_ldap_authentication?(password)
48
+ Devise::LDAP::Adapter.valid_credentials?(login_with, password)
49
+ end
50
+
51
+ def ldap_entry
52
+ @ldap_entry ||= Devise::LDAP::Adapter.get_ldap_entry(login_with)
53
+ end
54
+
55
+ def ldap_groups
56
+ Devise::LDAP::Adapter.get_groups(login_with)
57
+ end
58
+
59
+ def in_ldap_group?(group_name, group_attribute = LDAP::DEFAULT_GROUP_UNIQUE_MEMBER_LIST_KEY)
60
+ Devise::LDAP::Adapter.in_ldap_group?(login_with, group_name, group_attribute)
61
+ end
62
+
63
+ def ldap_dn
64
+ ldap_entry ? ldap_entry.dn : nil
65
+ end
66
+
67
+ def ldap_get_param(param)
68
+ if ldap_entry && !ldap_entry[param].empty?
69
+ value = ldap_entry.send(param)
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ #
76
+ # callbacks
77
+ #
78
+
79
+ # # Called before the ldap record is saved automatically
80
+ # def ldap_before_save
81
+ # end
82
+
83
+ # Called after a successful LDAP authentication
84
+ def after_ldap_authentication
85
+ end
86
+
87
+
88
+ module ClassMethods
89
+ # Find a user for ldap authentication.
90
+ def find_for_ldap_authentication(attributes={})
91
+ auth_key = self.authentication_keys.first
92
+ return nil unless attributes[auth_key].present?
93
+
94
+ auth_key_value = (self.case_insensitive_keys || []).include?(auth_key) ? attributes[auth_key].downcase : attributes[auth_key]
95
+ auth_key_value = (self.strip_whitespace_keys || []).include?(auth_key) ? auth_key_value.strip : auth_key_value
96
+
97
+ resource = where(auth_key => auth_key_value).first
98
+
99
+ if resource.blank?
100
+ resource = new
101
+ resource[auth_key] = auth_key_value
102
+ resource.password = attributes[:password]
103
+ end
104
+
105
+ if ::Devise.ldap_create_user && resource.new_record? && resource.valid_ldap_authentication?(attributes[:password])
106
+ resource.ldap_before_save if resource.respond_to?(:ldap_before_save)
107
+ resource.save!
108
+ end
109
+
110
+ resource
111
+ end
112
+
113
+ def update_with_password(resource)
114
+ puts "UPDATE_WITH_PASSWORD: #{resource.inspect}"
115
+ end
116
+
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,39 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class LdapAuthenticatable < Authenticatable
6
+
7
+ # Tests whether the returned resource exists in the database and the
8
+ # credentials are valid. If the resource is in the database and the credentials
9
+ # are valid, the user is authenticated. Otherwise failure messages are returned
10
+ # indicating whether the resource is not found in the database or the credentials
11
+ # are invalid.
12
+ def authenticate!
13
+ resource = mapping.to.find_for_ldap_authentication(authentication_hash.merge(password: password))
14
+
15
+ return fail(:invalid) unless resource
16
+
17
+ if resource.persisted?
18
+ if validate(resource) { resource.valid_ldap_authentication?(password) }
19
+ remember_me(resource)
20
+ resource.after_ldap_authentication
21
+ success!(resource)
22
+ else
23
+ return fail(:invalid) # Invalid credentials
24
+ end
25
+ end
26
+
27
+ if resource.new_record?
28
+ if validate(resource) { resource.valid_ldap_authentication?(password) }
29
+ return fail(:not_found_in_database) # Valid credentials
30
+ else
31
+ return fail(:invalid) # Invalid credentials
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)
@@ -0,0 +1,3 @@
1
+ module DeviseLdapAuthenticatable
2
+ VERSION = "0.8.5".freeze
3
+ end
@@ -0,0 +1,63 @@
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_group_membership_without_admin = false
38
+ # config.ldap_check_attributes = false
39
+ # config.ldap_use_admin_to_bind = false
40
+ # config.ldap_ad_group_check = false
41
+
42
+ eof
43
+ if options.advanced?
44
+ settings << <<-eof
45
+ # ==> Advanced LDAP Configuration
46
+ # config.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "\#{attribute}=\#{login},\#{ldap.base}" }
47
+
48
+ eof
49
+ end
50
+
51
+ settings
52
+ end
53
+
54
+ def rescue_from_exception
55
+ <<-eof
56
+ rescue_from DeviseLdapAuthenticatable::LdapException do |exception|
57
+ render :text => exception, :status => 500
58
+ end
59
+ eof
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,55 @@
1
+ ## Authorizations
2
+ # Uncomment out the merging for each environment that you'd like to include.
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 enviornment.
5
+ authorizations: &AUTHORIZATIONS
6
+ allow_unauthenticated_bind: false
7
+ group_base: ou=groups,dc=test,dc=com
8
+ ## Requires config.ldap_check_group_membership in devise.rb be true
9
+ # Can have multiple values, must match all to be authorized
10
+ required_groups:
11
+ # If only a group name is given, membership will be checked against "uniqueMember"
12
+ - cn=admins,ou=groups,dc=test,dc=com
13
+ - cn=users,ou=groups,dc=test,dc=com
14
+ # If an array is given, the first element will be the attribute to check against, the second the group name
15
+ - ["moreMembers", "cn=users,ou=groups,dc=test,dc=com"]
16
+ ## Requires config.ldap_check_attributes in devise.rb to be true
17
+ ## Can have multiple attributes and values, must match all to be authorized
18
+ require_attribute:
19
+ objectClass: inetOrgPerson
20
+ authorizationRole: postsAdmin
21
+
22
+ ## Environment
23
+
24
+ development:
25
+ host: localhost
26
+ port: 389
27
+ attribute: cn
28
+ base: ou=people,dc=test,dc=com
29
+ admin_user: cn=admin,dc=test,dc=com
30
+ admin_password: admin_password
31
+ ssl: false
32
+ additional_ldap_filter: "(&(objectClass=user)(sAMAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=CN=SomeActiveDirectoryGroup,OU=Groupies,cn=admin,dc=test,dc=com))"
33
+ # <<: *AUTHORIZATIONS
34
+
35
+ test:
36
+ host: localhost
37
+ port: 3389
38
+ attribute: cn
39
+ base: ou=people,dc=test,dc=com
40
+ admin_user: cn=admin,dc=test,dc=com
41
+ admin_password: admin_password
42
+ ssl: simple_tls
43
+ additional_ldap_filter: "(&(objectClass=user)(sAMAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=CN=SomeActiveDirectoryGroup,OU=Groupies,cn=admin,dc=test,dc=com))"
44
+ # <<: *AUTHORIZATIONS
45
+
46
+ production:
47
+ host: localhost
48
+ port: 636
49
+ attribute: cn
50
+ base: ou=people,dc=test,dc=com
51
+ admin_user: cn=admin,dc=test,dc=com
52
+ admin_password: admin_password
53
+ ssl: start_tls
54
+ additional_ldap_filter: "(&(objectClass=user)(sAMAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=CN=SomeActiveDirectoryGroup,OU=Groupies,cn=admin,dc=test,dc=com))"
55
+ # <<: *AUTHORIZATIONS
@@ -0,0 +1,2 @@
1
+ slapd-test.conf
2
+ slapd-ssl-test.conf
@@ -0,0 +1,73 @@
1
+ # ldapadd -x -h localhost -p 3389 -D "cn=admin,dc=test,dc=com" -w secret -f base.ldif
2
+
3
+ dn: dc=test,dc=com
4
+ objectClass: dcObject
5
+ objectClass: organizationalUnit
6
+ dc: test
7
+ ou: Test
8
+
9
+ dn: ou=people,dc=test,dc=com
10
+ objectClass: organizationalUnit
11
+ ou: people
12
+
13
+ dn: ou=others,dc=test,dc=com
14
+ objectClass: organizationalUnit
15
+ ou: others
16
+
17
+ dn: ou=groups,dc=test,dc=com
18
+ objectClass: organizationalUnit
19
+ ou: groups
20
+
21
+ # example.user@test.com, people, test.com
22
+ dn: cn=example.user@test.com,ou=people,dc=test,dc=com
23
+ objectClass: inetOrgPerson
24
+ objectClass: authorizations
25
+ sn: User
26
+ uid: example_user
27
+ mail: example.user@test.com
28
+ cn: example.user@test.com
29
+ authorizationRole: blogUser
30
+ userPassword:: e1NTSEF9ZXRYaE9NcjRjOGFiTjlqYUxyczZKSll5MFlaZUF1NURCVWhhY0E9PQ=
31
+ =
32
+
33
+ # other.user@test.com
34
+ dn: cn=other.user@test.com,ou=others,dc=test,dc=com
35
+ objectClass: inetOrgPerson
36
+ objectClass: authorizations
37
+ objectClass: organizationalPerson
38
+ objectClass: person
39
+ objectClass: top
40
+ sn: Other
41
+ uid: other_user
42
+ cn: other.user@test.com
43
+ authorizationRole: blogUser
44
+ userPassword:: e1NIQX1IQXdtdk13RGF1ZUpyZDhwakxXMzZ6Yi9jTUU9
45
+
46
+ # example.admin@test.com, people, test.com
47
+ dn: cn=example.admin@test.com,ou=people,dc=test,dc=com
48
+ objectClass: inetOrgPerson
49
+ objectClass: authorizations
50
+ objectClass: organizationalPerson
51
+ objectClass: person
52
+ objectClass: top
53
+ sn: Admin
54
+ uid: example_admin
55
+ cn: example.admin@test.com
56
+ authorizationRole: blogAdmin
57
+ userPassword:: e1NIQX0wcUNXaERISGFwWmc3ekJxZWRRanBzNW1EUDA9
58
+
59
+ # users, groups, test.com
60
+ dn: cn=users,ou=groups,dc=test,dc=com
61
+ objectClass: authorizations
62
+ objectClass: groupOfUniqueNames
63
+ objectClass: top
64
+ uniqueMember: cn=example.user@test.com,ou=people,dc=test,dc=com
65
+ authorizationRole: cn=example.admin@test.com,ou=people,dc=test,dc=com
66
+ cn: users
67
+
68
+ # users, groups, test.com
69
+ dn: cn=admins,ou=groups,dc=test,dc=com
70
+ objectClass: groupOfUniqueNames
71
+ objectClass: top
72
+ uniqueMember: cn=example.admin@test.com,ou=people,dc=test,dc=com
73
+ cn: admins
@@ -0,0 +1,26 @@
1
+ dn: cn=admins,ou=groups,dc=test,dc=com
2
+ changetype: delete
3
+
4
+ dn: cn=users,ou=groups,dc=test,dc=com
5
+ changetype: delete
6
+
7
+ dn: cn=example.admin@test.com,ou=people,dc=test,dc=com
8
+ changetype: delete
9
+
10
+ dn: cn=example.user@test.com,ou=people,dc=test,dc=com
11
+ changetype: delete
12
+
13
+ dn: cn=other.user@test.com,ou=others,dc=test,dc=com
14
+ changetype: delete
15
+
16
+ dn: ou=groups,dc=test,dc=com
17
+ changetype: delete
18
+
19
+ dn: ou=people,dc=test,dc=com
20
+ changetype: delete
21
+
22
+ dn: ou=others,dc=test,dc=com
23
+ changetype: delete
24
+
25
+ dn: dc=test,dc=com
26
+ changetype: delete
@@ -0,0 +1,6 @@
1
+ attributetype ( 1.1.2.2.5 NAME 'authorizationRole' SUP name )
2
+
3
+ objectclass ( 1.1.2.2.1 NAME 'authorizations'
4
+ DESC 'mixin authorizations'
5
+ AUXILIARY
6
+ MAY authorizationRole )
@@ -0,0 +1,2 @@
1
+ dc=test,dc=com
2
+ dc=test,dc=com.ldif
@@ -0,0 +1,2 @@
1
+ slapd.pid
2
+ slapd.args
File without changes
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+
6
+ FileUtils.chdir(File.dirname(__FILE__))
7
+
8
+ ## For OSX:
9
+ ENV['PATH'] = "#{ENV['PATH']}:/usr/libexec"
10
+
11
+ template = File.read('slapd-test.conf.erb')
12
+ normal_out = 'slapd-test.conf'
13
+ ssl_out = 'slapd-ssl-test.conf'
14
+
15
+ File.open(normal_out, 'w') do |f|
16
+ @ssl = false
17
+ f.write ERB.new(template).result(binding)
18
+ end
19
+ File.open(ssl_out, 'w') do |f|
20
+ @ssl = true
21
+ f.write ERB.new(template).result(binding)
22
+ end
23
+
24
+ if ARGV.first == '--ssl'
25
+ cmd = "slapd -d 1 -f #{ssl_out} -h ldaps://localhost:3389"
26
+ else
27
+ cmd = "slapd -d 1 -f #{normal_out} -h ldap://localhost:3389"
28
+ end
29
+
30
+ puts(cmd)
31
+ exec(cmd)