af-devise 2.1.2

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 (207) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +15 -0
  3. data/CHANGELOG.rdoc +885 -0
  4. data/CONTRIBUTING.md +14 -0
  5. data/Gemfile +29 -0
  6. data/Gemfile.lock +155 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +394 -0
  9. data/Rakefile +34 -0
  10. data/app/controllers/devise/confirmations_controller.rb +43 -0
  11. data/app/controllers/devise/omniauth_callbacks_controller.rb +30 -0
  12. data/app/controllers/devise/passwords_controller.rb +65 -0
  13. data/app/controllers/devise/registrations_controller.rb +119 -0
  14. data/app/controllers/devise/sessions_controller.rb +50 -0
  15. data/app/controllers/devise/unlocks_controller.rb +44 -0
  16. data/app/controllers/devise_controller.rb +184 -0
  17. data/app/helpers/devise_helper.rb +25 -0
  18. data/app/mailers/devise/mailer.rb +15 -0
  19. data/app/views/devise/_links.erb +3 -0
  20. data/app/views/devise/confirmations/new.html.erb +12 -0
  21. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  22. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  23. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  24. data/app/views/devise/passwords/edit.html.erb +16 -0
  25. data/app/views/devise/passwords/new.html.erb +12 -0
  26. data/app/views/devise/registrations/edit.html.erb +25 -0
  27. data/app/views/devise/registrations/new.html.erb +18 -0
  28. data/app/views/devise/sessions/new.html.erb +17 -0
  29. data/app/views/devise/shared/_links.erb +25 -0
  30. data/app/views/devise/unlocks/new.html.erb +12 -0
  31. data/config/locales/en.yml +59 -0
  32. data/devise.gemspec +25 -0
  33. data/gemfiles/Gemfile.rails-3.1.x +35 -0
  34. data/gemfiles/Gemfile.rails-3.1.x.lock +167 -0
  35. data/lib/devise.rb +444 -0
  36. data/lib/devise/controllers/helpers.rb +285 -0
  37. data/lib/devise/controllers/rememberable.rb +52 -0
  38. data/lib/devise/controllers/scoped_views.rb +17 -0
  39. data/lib/devise/controllers/url_helpers.rb +67 -0
  40. data/lib/devise/delegator.rb +16 -0
  41. data/lib/devise/failure_app.rb +187 -0
  42. data/lib/devise/hooks/activatable.rb +11 -0
  43. data/lib/devise/hooks/forgetable.rb +9 -0
  44. data/lib/devise/hooks/lockable.rb +7 -0
  45. data/lib/devise/hooks/rememberable.rb +6 -0
  46. data/lib/devise/hooks/timeoutable.rb +25 -0
  47. data/lib/devise/hooks/trackable.rb +9 -0
  48. data/lib/devise/mailers/helpers.rb +91 -0
  49. data/lib/devise/mapping.rb +172 -0
  50. data/lib/devise/models.rb +128 -0
  51. data/lib/devise/models/authenticatable.rb +268 -0
  52. data/lib/devise/models/confirmable.rb +270 -0
  53. data/lib/devise/models/database_authenticatable.rb +127 -0
  54. data/lib/devise/models/lockable.rb +193 -0
  55. data/lib/devise/models/omniauthable.rb +27 -0
  56. data/lib/devise/models/recoverable.rb +140 -0
  57. data/lib/devise/models/registerable.rb +25 -0
  58. data/lib/devise/models/rememberable.rb +125 -0
  59. data/lib/devise/models/timeoutable.rb +49 -0
  60. data/lib/devise/models/token_authenticatable.rb +89 -0
  61. data/lib/devise/models/trackable.rb +35 -0
  62. data/lib/devise/models/validatable.rb +66 -0
  63. data/lib/devise/modules.rb +29 -0
  64. data/lib/devise/omniauth.rb +28 -0
  65. data/lib/devise/omniauth/config.rb +45 -0
  66. data/lib/devise/omniauth/url_helpers.rb +18 -0
  67. data/lib/devise/orm/active_record.rb +3 -0
  68. data/lib/devise/orm/mongoid.rb +3 -0
  69. data/lib/devise/param_filter.rb +41 -0
  70. data/lib/devise/rails.rb +54 -0
  71. data/lib/devise/rails/routes.rb +446 -0
  72. data/lib/devise/rails/warden_compat.rb +43 -0
  73. data/lib/devise/strategies/authenticatable.rb +176 -0
  74. data/lib/devise/strategies/base.rb +20 -0
  75. data/lib/devise/strategies/database_authenticatable.rb +20 -0
  76. data/lib/devise/strategies/rememberable.rb +55 -0
  77. data/lib/devise/strategies/token_authenticatable.rb +56 -0
  78. data/lib/devise/test_helpers.rb +131 -0
  79. data/lib/devise/time_inflector.rb +14 -0
  80. data/lib/devise/version.rb +3 -0
  81. data/lib/generators/active_record/devise_generator.rb +79 -0
  82. data/lib/generators/active_record/templates/migration.rb +19 -0
  83. data/lib/generators/active_record/templates/migration_existing.rb +26 -0
  84. data/lib/generators/devise/devise_generator.rb +24 -0
  85. data/lib/generators/devise/install_generator.rb +24 -0
  86. data/lib/generators/devise/orm_helpers.rb +32 -0
  87. data/lib/generators/devise/views_generator.rb +116 -0
  88. data/lib/generators/mongoid/devise_generator.rb +57 -0
  89. data/lib/generators/templates/README +35 -0
  90. data/lib/generators/templates/devise.rb +240 -0
  91. data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  92. data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  93. data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
  94. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +15 -0
  95. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
  96. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
  97. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +22 -0
  98. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
  99. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
  100. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +15 -0
  101. data/test/controllers/custom_strategy_test.rb +62 -0
  102. data/test/controllers/helpers_test.rb +253 -0
  103. data/test/controllers/internal_helpers_test.rb +110 -0
  104. data/test/controllers/sessions_controller_test.rb +85 -0
  105. data/test/controllers/url_helpers_test.rb +59 -0
  106. data/test/delegator_test.rb +19 -0
  107. data/test/devise_test.rb +72 -0
  108. data/test/failure_app_test.rb +221 -0
  109. data/test/generators/active_record_generator_test.rb +75 -0
  110. data/test/generators/devise_generator_test.rb +39 -0
  111. data/test/generators/install_generator_test.rb +13 -0
  112. data/test/generators/mongoid_generator_test.rb +23 -0
  113. data/test/generators/views_generator_test.rb +52 -0
  114. data/test/helpers/devise_helper_test.rb +51 -0
  115. data/test/integration/authenticatable_test.rb +633 -0
  116. data/test/integration/confirmable_test.rb +298 -0
  117. data/test/integration/database_authenticatable_test.rb +82 -0
  118. data/test/integration/http_authenticatable_test.rb +97 -0
  119. data/test/integration/lockable_test.rb +242 -0
  120. data/test/integration/omniauthable_test.rb +133 -0
  121. data/test/integration/recoverable_test.rb +334 -0
  122. data/test/integration/registerable_test.rb +345 -0
  123. data/test/integration/rememberable_test.rb +158 -0
  124. data/test/integration/timeoutable_test.rb +140 -0
  125. data/test/integration/token_authenticatable_test.rb +161 -0
  126. data/test/integration/trackable_test.rb +92 -0
  127. data/test/mailers/confirmation_instructions_test.rb +102 -0
  128. data/test/mailers/reset_password_instructions_test.rb +83 -0
  129. data/test/mailers/unlock_instructions_test.rb +77 -0
  130. data/test/mapping_test.rb +127 -0
  131. data/test/models/authenticatable_test.rb +7 -0
  132. data/test/models/confirmable_test.rb +391 -0
  133. data/test/models/database_authenticatable_test.rb +196 -0
  134. data/test/models/lockable_test.rb +273 -0
  135. data/test/models/omniauthable_test.rb +7 -0
  136. data/test/models/recoverable_test.rb +205 -0
  137. data/test/models/registerable_test.rb +7 -0
  138. data/test/models/rememberable_test.rb +174 -0
  139. data/test/models/serializable_test.rb +49 -0
  140. data/test/models/timeoutable_test.rb +46 -0
  141. data/test/models/token_authenticatable_test.rb +55 -0
  142. data/test/models/trackable_test.rb +13 -0
  143. data/test/models/validatable_test.rb +117 -0
  144. data/test/models_test.rb +179 -0
  145. data/test/omniauth/config_test.rb +57 -0
  146. data/test/omniauth/url_helpers_test.rb +51 -0
  147. data/test/orm/active_record.rb +9 -0
  148. data/test/orm/mongoid.rb +13 -0
  149. data/test/rails_app/Rakefile +10 -0
  150. data/test/rails_app/app/active_record/admin.rb +6 -0
  151. data/test/rails_app/app/active_record/shim.rb +2 -0
  152. data/test/rails_app/app/active_record/user.rb +6 -0
  153. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  154. data/test/rails_app/app/controllers/admins_controller.rb +11 -0
  155. data/test/rails_app/app/controllers/application_controller.rb +8 -0
  156. data/test/rails_app/app/controllers/home_controller.rb +25 -0
  157. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  158. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  159. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
  160. data/test/rails_app/app/controllers/users_controller.rb +23 -0
  161. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  162. data/test/rails_app/app/mailers/users/mailer.rb +8 -0
  163. data/test/rails_app/app/mongoid/admin.rb +29 -0
  164. data/test/rails_app/app/mongoid/shim.rb +24 -0
  165. data/test/rails_app/app/mongoid/user.rb +42 -0
  166. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  167. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  168. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
  169. data/test/rails_app/app/views/home/index.html.erb +1 -0
  170. data/test/rails_app/app/views/home/join.html.erb +1 -0
  171. data/test/rails_app/app/views/home/private.html.erb +1 -0
  172. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
  173. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  174. data/test/rails_app/app/views/users/index.html.erb +1 -0
  175. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  176. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  177. data/test/rails_app/config.ru +4 -0
  178. data/test/rails_app/config/application.rb +41 -0
  179. data/test/rails_app/config/boot.rb +8 -0
  180. data/test/rails_app/config/database.yml +18 -0
  181. data/test/rails_app/config/environment.rb +5 -0
  182. data/test/rails_app/config/environments/development.rb +18 -0
  183. data/test/rails_app/config/environments/production.rb +33 -0
  184. data/test/rails_app/config/environments/test.rb +33 -0
  185. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  186. data/test/rails_app/config/initializers/devise.rb +178 -0
  187. data/test/rails_app/config/initializers/inflections.rb +2 -0
  188. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  189. data/test/rails_app/config/routes.rb +100 -0
  190. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +74 -0
  191. data/test/rails_app/db/schema.rb +52 -0
  192. data/test/rails_app/lib/shared_admin.rb +14 -0
  193. data/test/rails_app/lib/shared_user.rb +26 -0
  194. data/test/rails_app/public/404.html +26 -0
  195. data/test/rails_app/public/422.html +26 -0
  196. data/test/rails_app/public/500.html +26 -0
  197. data/test/rails_app/public/favicon.ico +0 -0
  198. data/test/rails_app/script/rails +10 -0
  199. data/test/routes_test.rb +248 -0
  200. data/test/support/assertions.rb +40 -0
  201. data/test/support/helpers.rb +91 -0
  202. data/test/support/integration.rb +92 -0
  203. data/test/support/locale/en.yml +4 -0
  204. data/test/support/webrat/integrations/rails.rb +24 -0
  205. data/test/test_helper.rb +27 -0
  206. data/test/test_helpers_test.rb +151 -0
  207. metadata +421 -0
@@ -0,0 +1,172 @@
1
+ module Devise
2
+ # Responsible for handling devise mappings and routes configuration. Each
3
+ # resource configured by devise_for in routes is actually creating a mapping
4
+ # object. You can refer to devise_for in routes for usage options.
5
+ #
6
+ # The required value in devise_for is actually not used internally, but it's
7
+ # inflected to find all other values.
8
+ #
9
+ # map.devise_for :users
10
+ # mapping = Devise.mappings[:user]
11
+ #
12
+ # mapping.name #=> :user
13
+ # # is the scope used in controllers and warden, given in the route as :singular.
14
+ #
15
+ # mapping.as #=> "users"
16
+ # # how the mapping should be search in the path, given in the route as :as.
17
+ #
18
+ # mapping.to #=> User
19
+ # # is the class to be loaded from routes, given in the route as :class_name.
20
+ #
21
+ # mapping.modules #=> [:authenticatable]
22
+ # # is the modules included in the class
23
+ #
24
+ class Mapping #:nodoc:
25
+ attr_reader :singular, :scoped_path, :path, :controllers, :path_names,
26
+ :class_name, :sign_out_via, :format, :used_routes, :used_helpers, :failure_app
27
+
28
+ alias :name :singular
29
+
30
+ # Receives an object and find a scope for it. If a scope cannot be found,
31
+ # raises an error. If a symbol is given, it's considered to be the scope.
32
+ def self.find_scope!(duck)
33
+ case duck
34
+ when String, Symbol
35
+ return duck
36
+ when Class
37
+ Devise.mappings.each_value { |m| return m.name if duck <= m.to }
38
+ else
39
+ Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
40
+ end
41
+
42
+ raise "Could not find a valid mapping for #{duck.inspect}"
43
+ end
44
+
45
+ def self.find_by_path!(path, path_type=:fullpath)
46
+ Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
47
+ raise "Could not find a valid mapping for path #{path.inspect}"
48
+ end
49
+
50
+ def initialize(name, options) #:nodoc:
51
+ @scoped_path = options[:as] ? "#{options[:as]}/#{name}" : name.to_s
52
+ @singular = (options[:singular] || @scoped_path.tr('/', '_').singularize).to_sym
53
+
54
+ @class_name = (options[:class_name] || name.to_s.classify).to_s
55
+ @klass = Devise.ref(@class_name)
56
+
57
+ @path = (options[:path] || name).to_s
58
+ @path_prefix = options[:path_prefix]
59
+
60
+ @sign_out_via = options[:sign_out_via] || Devise.sign_out_via
61
+ @format = options[:format]
62
+
63
+ default_failure_app(options)
64
+ default_controllers(options)
65
+ default_path_names(options)
66
+ default_used_route(options)
67
+ default_used_helpers(options)
68
+ end
69
+
70
+ # Return modules for the mapping.
71
+ def modules
72
+ @modules ||= to.respond_to?(:devise_modules) ? to.devise_modules : []
73
+ end
74
+
75
+ # Gives the class the mapping points to.
76
+ def to
77
+ @klass.get
78
+ end
79
+
80
+ def strategies
81
+ @strategies ||= STRATEGIES.values_at(*self.modules).compact.uniq.reverse
82
+ end
83
+
84
+ def no_input_strategies
85
+ self.strategies & Devise::NO_INPUT
86
+ end
87
+
88
+ def routes
89
+ @routes ||= ROUTES.values_at(*self.modules).compact.uniq
90
+ end
91
+
92
+ def authenticatable?
93
+ @authenticatable ||= self.modules.any? { |m| m.to_s =~ /authenticatable/ }
94
+ end
95
+
96
+ def fullpath
97
+ "/#{@path_prefix}/#{@path}".squeeze("/")
98
+ end
99
+
100
+ # Create magic predicates for verifying what module is activated by this map.
101
+ # Example:
102
+ #
103
+ # def confirmable?
104
+ # self.modules.include?(:confirmable)
105
+ # end
106
+ #
107
+ def self.add_module(m)
108
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
109
+ def #{m}?
110
+ self.modules.include?(:#{m})
111
+ end
112
+ METHOD
113
+ end
114
+
115
+ private
116
+
117
+ def default_failure_app(options)
118
+ @failure_app = options[:failure_app] || Devise::FailureApp
119
+ if @failure_app.is_a?(String)
120
+ ref = Devise.ref(@failure_app)
121
+ @failure_app = lambda { |env| ref.get.call(env) }
122
+ end
123
+ end
124
+
125
+ def default_controllers(options)
126
+ mod = options[:module] || "devise"
127
+ @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
128
+ @controllers.merge!(options[:controllers]) if options[:controllers]
129
+ @controllers.each { |k,v| @controllers[k] = v.to_s }
130
+ end
131
+
132
+ def default_path_names(options)
133
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
134
+ @path_names[:registration] = ""
135
+ @path_names.merge!(options[:path_names]) if options[:path_names]
136
+ end
137
+
138
+ def default_constraints(options)
139
+ @constraints = Hash.new
140
+ @constraints.merge!(options[:constraints]) if options[:constraints]
141
+ end
142
+
143
+ def default_defaults(options)
144
+ @defaults = Hash.new
145
+ @defaults.merge!(options[:defaults]) if options[:defaults]
146
+ end
147
+
148
+ def default_used_route(options)
149
+ singularizer = lambda { |s| s.to_s.singularize.to_sym }
150
+
151
+ if options.has_key?(:only)
152
+ @used_routes = self.routes & Array(options[:only]).map(&singularizer)
153
+ elsif options[:skip] == :all
154
+ @used_routes = []
155
+ else
156
+ @used_routes = self.routes - Array(options[:skip]).map(&singularizer)
157
+ end
158
+ end
159
+
160
+ def default_used_helpers(options)
161
+ singularizer = lambda { |s| s.to_s.singularize.to_sym }
162
+
163
+ if options[:skip_helpers] == true
164
+ @used_helpers = @used_routes
165
+ elsif skip = options[:skip_helpers]
166
+ @used_helpers = self.routes - Array(skip).map(&singularizer)
167
+ else
168
+ @used_helpers = self.routes
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,128 @@
1
+ module Devise
2
+ module Models
3
+ class MissingAttribute < StandardError
4
+ def initialize(attributes)
5
+ @attributes = attributes
6
+ end
7
+
8
+ def message
9
+ "The following attribute(s) is (are) missing on your model: #{@attributes.join(", ")}"
10
+ end
11
+ end
12
+
13
+ # Creates configuration values for Devise and for the given module.
14
+ #
15
+ # Devise::Models.config(Devise::Authenticatable, :stretches, 10)
16
+ #
17
+ # The line above creates:
18
+ #
19
+ # 1) An accessor called Devise.stretches, which value is used by default;
20
+ #
21
+ # 2) Some class methods for your model Model.stretches and Model.stretches=
22
+ # which have higher priority than Devise.stretches;
23
+ #
24
+ # 3) And an instance method stretches.
25
+ #
26
+ # To add the class methods you need to have a module ClassMethods defined
27
+ # inside the given class.
28
+ #
29
+ def self.config(mod, *accessors) #:nodoc:
30
+ class << mod; attr_accessor :available_configs; end
31
+ mod.available_configs = accessors
32
+
33
+ accessors.each do |accessor|
34
+ mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
35
+ def #{accessor}
36
+ if defined?(@#{accessor})
37
+ @#{accessor}
38
+ elsif superclass.respond_to?(:#{accessor})
39
+ superclass.#{accessor}
40
+ else
41
+ Devise.#{accessor}
42
+ end
43
+ end
44
+
45
+ def #{accessor}=(value)
46
+ @#{accessor} = value
47
+ end
48
+ METHOD
49
+ end
50
+ end
51
+
52
+ def self.check_fields!(klass)
53
+ failed_attributes = []
54
+ instance = klass.new
55
+
56
+ klass.devise_modules.each do |mod|
57
+ constant = const_get(mod.to_s.classify)
58
+
59
+ if constant.respond_to?(:required_fields)
60
+ constant.required_fields(klass).each do |field|
61
+ failed_attributes << field unless instance.respond_to?(field)
62
+ end
63
+ else
64
+ ActiveSupport::Deprecation.warn "The module #{mod} doesn't implement self.required_fields(klass). " \
65
+ "Devise uses required_fields to warn developers of any missing fields in their models. " \
66
+ "Please implement #{mod}.required_fields(klass) that returns an array of symbols with the required fields."
67
+ end
68
+ end
69
+
70
+ if failed_attributes.any?
71
+ fail Devise::Models::MissingAttribute.new(failed_attributes)
72
+ end
73
+ end
74
+
75
+ # Include the chosen devise modules in your model:
76
+ #
77
+ # devise :database_authenticatable, :confirmable, :recoverable
78
+ #
79
+ # You can also give any of the devise configuration values in form of a hash,
80
+ # with specific values for this model. Please check your Devise initializer
81
+ # for a complete description on those values.
82
+ #
83
+ def devise(*modules)
84
+ options = modules.extract_options!.dup
85
+
86
+ selected_modules = modules.map(&:to_sym).uniq.sort_by do |s|
87
+ Devise::ALL.index(s) || -1 # follow Devise::ALL order
88
+ end
89
+
90
+ devise_modules_hook! do
91
+ include Devise::Models::Authenticatable
92
+ selected_modules.each do |m|
93
+ if m == :encryptable && !(defined?(Devise::Models::Encryptable))
94
+ warn "[DEVISE] You're trying to include :encryptable in your model but it is not bundled with the Devise gem anymore. Please add `devise-encryptable` to your Gemfile to proceed.\n"
95
+ end
96
+
97
+ mod = Devise::Models.const_get(m.to_s.classify)
98
+
99
+ if mod.const_defined?("ClassMethods")
100
+ class_mod = mod.const_get("ClassMethods")
101
+ extend class_mod
102
+
103
+ if class_mod.respond_to?(:available_configs)
104
+ available_configs = class_mod.available_configs
105
+ available_configs.each do |config|
106
+ next unless options.key?(config)
107
+ send(:"#{config}=", options.delete(config))
108
+ end
109
+ end
110
+ end
111
+
112
+ include mod
113
+ end
114
+
115
+ self.devise_modules |= selected_modules
116
+ options.each { |key, value| send(:"#{key}=", value) }
117
+ end
118
+ end
119
+
120
+ # The hook which is called inside devise.
121
+ # So your ORM can include devise compatibility stuff.
122
+ def devise_modules_hook!
123
+ yield
124
+ end
125
+ end
126
+ end
127
+
128
+ require 'devise/models/authenticatable'
@@ -0,0 +1,268 @@
1
+ require 'devise/hooks/activatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Authenticatable module. Holds common settings for authentication.
6
+ #
7
+ # == Options
8
+ #
9
+ # Authenticatable adds the following options to devise_for:
10
+ #
11
+ # * +authentication_keys+: parameters used for authentication. By default [:email].
12
+ #
13
+ # * +request_keys+: parameters from the request object used for authentication.
14
+ # By specifying a symbol (which should be a request method), it will automatically be
15
+ # passed to find_for_authentication method and considered in your model lookup.
16
+ #
17
+ # For instance, if you set :request_keys to [:subdomain], :subdomain will be considered
18
+ # as key on authentication. This can also be a hash where the value is a boolean expliciting
19
+ # if the value is required or not.
20
+ #
21
+ # * +http_authenticatable+: if this model allows http authentication. By default true.
22
+ # It also accepts an array specifying the strategies that should allow http.
23
+ #
24
+ # * +params_authenticatable+: if this model allows authentication through request params. By default true.
25
+ # It also accepts an array specifying the strategies that should allow params authentication.
26
+ #
27
+ # * +skip_session_storage+: By default Devise will store the user in session.
28
+ # You can skip storage for http and token auth by appending values to array:
29
+ # :skip_session_storage => [:token_auth] or :skip_session_storage => [:http_auth, :token_auth],
30
+ # by default is set to :skip_session_storage => [:http_auth].
31
+ #
32
+ # == active_for_authentication?
33
+ #
34
+ # After authenticating a user and in each request, Devise checks if your model is active by
35
+ # calling model.active_for_authentication?. This method is overwriten by other devise modules. For instance,
36
+ # :confirmable overwrites .active_for_authentication? to only return true if your model was confirmed.
37
+ #
38
+ # You overwrite this method yourself, but if you do, don't forget to call super:
39
+ #
40
+ # def active_for_authentication?
41
+ # super && special_condition_is_valid?
42
+ # end
43
+ #
44
+ # Whenever active_for_authentication? returns false, Devise asks the reason why your model is inactive using
45
+ # the inactive_message method. You can overwrite it as well:
46
+ #
47
+ # def inactive_message
48
+ # special_condition_is_valid? ? super : :special_condition_is_not_valid
49
+ # end
50
+ #
51
+ module Authenticatable
52
+ extend ActiveSupport::Concern
53
+
54
+ BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
55
+ :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
56
+ :last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
57
+ :remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at, :authentication_token]
58
+
59
+ included do
60
+ class_attribute :devise_modules, :instance_writer => false
61
+ self.devise_modules ||= []
62
+
63
+ before_validation :downcase_keys
64
+ before_validation :strip_whitespace
65
+ end
66
+
67
+ def self.required_fields(klass)
68
+ []
69
+ end
70
+
71
+ # Check if the current object is valid for authentication. This method and
72
+ # find_for_authentication are the methods used in a Warden::Strategy to check
73
+ # if a model should be signed in or not.
74
+ #
75
+ # However, you should not overwrite this method, you should overwrite active_for_authentication?
76
+ # and inactive_message instead.
77
+ def valid_for_authentication?
78
+ block_given? ? yield : true
79
+ end
80
+
81
+ def unauthenticated_message
82
+ :invalid
83
+ end
84
+
85
+ def active_for_authentication?
86
+ true
87
+ end
88
+
89
+ def inactive_message
90
+ :inactive
91
+ end
92
+
93
+ def authenticatable_salt
94
+ end
95
+
96
+ def headers_for(name)
97
+ {}
98
+ end
99
+
100
+ array = %w(serializable_hash)
101
+ # to_xml does not call serializable_hash on 3.1
102
+ array << "to_xml" if Rails::VERSION::STRING[0,3] == "3.1"
103
+
104
+ array.each do |method|
105
+ class_eval <<-RUBY, __FILE__, __LINE__
106
+ # Redefine to_xml and serializable_hash in models for more secure defaults.
107
+ # By default, it removes from the serializable model all attributes that
108
+ # are *not* accessible. You can remove this default by using :force_except
109
+ # and passing a new list of attributes you want to exempt. All attributes
110
+ # given to :except will simply add names to exempt to Devise internal list.
111
+ def #{method}(options=nil)
112
+ options ||= {}
113
+ options[:except] = Array(options[:except])
114
+
115
+ if options[:force_except]
116
+ options[:except].concat Array(options[:force_except])
117
+ else
118
+ options[:except].concat BLACKLIST_FOR_SERIALIZATION
119
+ end
120
+ super(options)
121
+ end
122
+ RUBY
123
+ end
124
+
125
+ protected
126
+
127
+ def devise_mailer
128
+ Devise.mailer
129
+ end
130
+
131
+ # This is an internal method called every time Devise needs
132
+ # to send a notification/mail. This can be overriden if you
133
+ # need to customize the e-mail delivery logic. For instance,
134
+ # if you are using a queue to deliver e-mails (delayed job,
135
+ # sidekiq, resque, etc), you must add the delivery to the queue
136
+ # just after the transaction was committed. To achieve this,
137
+ # you can override send_devise_notification to store the
138
+ # deliveries until the after_commit callback is triggered:
139
+ #
140
+ # class User
141
+ # devise :database_authenticatable, :confirmable
142
+ #
143
+ # after_commit :send_pending_notifications
144
+ #
145
+ # protected
146
+ #
147
+ # def send_devise_notification(notification)
148
+ # pending_notifications << notification
149
+ # end
150
+ #
151
+ # def send_pending_notifications
152
+ # pending_notifications.each do |n|
153
+ # devise_mailer.send(n, self).deliver
154
+ # end
155
+ # end
156
+ #
157
+ # def pending_notifications
158
+ # @pending_notifications ||= []
159
+ # end
160
+ # end
161
+ #
162
+ def send_devise_notification(notification)
163
+ devise_mailer.send(notification, self).deliver
164
+ end
165
+
166
+ def downcase_keys
167
+ self.class.case_insensitive_keys.each { |k| self[k].try(:downcase!) }
168
+ end
169
+
170
+ def strip_whitespace
171
+ self.class.strip_whitespace_keys.each { |k| self[k].try(:strip!) }
172
+ end
173
+
174
+ module ClassMethods
175
+ Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys,
176
+ :case_insensitive_keys, :http_authenticatable, :params_authenticatable, :skip_session_storage)
177
+
178
+ def serialize_into_session(record)
179
+ [record.to_key, record.authenticatable_salt]
180
+ end
181
+
182
+ def serialize_from_session(key, salt)
183
+ record = to_adapter.get(key)
184
+ record if record && record.authenticatable_salt == salt
185
+ end
186
+
187
+ def params_authenticatable?(strategy)
188
+ params_authenticatable.is_a?(Array) ?
189
+ params_authenticatable.include?(strategy) : params_authenticatable
190
+ end
191
+
192
+ def http_authenticatable?(strategy)
193
+ http_authenticatable.is_a?(Array) ?
194
+ http_authenticatable.include?(strategy) : http_authenticatable
195
+ end
196
+
197
+ # Find first record based on conditions given (ie by the sign in form).
198
+ # This method is always called during an authentication process but
199
+ # it may be wrapped as well. For instance, database authenticatable
200
+ # provides a `find_for_database_authentication` that wraps a call to
201
+ # this method. This allows you to customize both database authenticatable
202
+ # or the whole authenticate stack by customize `find_for_authentication.`
203
+ #
204
+ # Overwrite to add customized conditions, create a join, or maybe use a
205
+ # namedscope to filter records while authenticating.
206
+ # Example:
207
+ #
208
+ # def self.find_for_authentication(conditions={})
209
+ # conditions[:active] = true
210
+ # super
211
+ # end
212
+ #
213
+ # Finally, notice that Devise also queries for users in other scenarios
214
+ # besides authentication, for example when retrieving an user to send
215
+ # an e-mail for password reset. In such cases, find_for_authentication
216
+ # is not called.
217
+ def find_for_authentication(conditions)
218
+ find_first_by_auth_conditions(conditions)
219
+ end
220
+
221
+ def find_first_by_auth_conditions(conditions)
222
+ to_adapter.find_first devise_param_filter.filter(conditions)
223
+ end
224
+
225
+ # Find an initialize a record setting an error if it can't be found.
226
+ def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
227
+ find_or_initialize_with_errors([attribute], { attribute => value }, error)
228
+ end
229
+
230
+ # Find an initialize a group of attributes based on a list of required attributes.
231
+ def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
232
+ attributes = attributes.slice(*required_attributes)
233
+ attributes.delete_if { |key, value| value.blank? }
234
+
235
+ if attributes.size == required_attributes.size
236
+ record = find_first_by_auth_conditions(attributes)
237
+ end
238
+
239
+ unless record
240
+ record = new
241
+
242
+ required_attributes.each do |key|
243
+ value = attributes[key]
244
+ record.send("#{key}=", value)
245
+ record.errors.add(key, value.present? ? error : :blank)
246
+ end
247
+ end
248
+
249
+ record
250
+ end
251
+
252
+ protected
253
+
254
+ def devise_param_filter
255
+ @devise_param_filter ||= Devise::ParamFilter.new(case_insensitive_keys, strip_whitespace_keys)
256
+ end
257
+
258
+ # Generate a token by looping and ensuring does not already exist.
259
+ def generate_token(column)
260
+ loop do
261
+ token = Devise.friendly_token
262
+ break token unless to_adapter.find_first({ column => token })
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end