devise-warbler 2.2.3

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 (208) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +35 -0
  3. data/CHANGELOG.rdoc +923 -0
  4. data/CONTRIBUTING.md +14 -0
  5. data/Gemfile +32 -0
  6. data/Gemfile.lock +156 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +396 -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 +48 -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 +29 -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 +24 -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 +451 -0
  36. data/lib/devise/controllers/helpers.rb +285 -0
  37. data/lib/devise/controllers/rememberable.rb +56 -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 +95 -0
  49. data/lib/devise/mapping.rb +172 -0
  50. data/lib/devise/models.rb +128 -0
  51. data/lib/devise/models/authenticatable.rb +276 -0
  52. data/lib/devise/models/confirmable.rb +267 -0
  53. data/lib/devise/models/database_authenticatable.rb +126 -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 +40 -0
  70. data/lib/devise/rails.rb +51 -0
  71. data/lib/devise/rails/routes.rb +448 -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 +122 -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 +16 -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 +27 -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 +16 -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 +83 -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 +67 -0
  114. data/test/helpers/devise_helper_test.rb +51 -0
  115. data/test/integration/authenticatable_test.rb +687 -0
  116. data/test/integration/confirmable_test.rb +299 -0
  117. data/test/integration/database_authenticatable_test.rb +84 -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 +347 -0
  123. data/test/integration/rememberable_test.rb +165 -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 +106 -0
  128. data/test/mailers/reset_password_instructions_test.rb +87 -0
  129. data/test/mailers/unlock_instructions_test.rb +82 -0
  130. data/test/mapping_test.rb +127 -0
  131. data/test/models/authenticatable_test.rb +13 -0
  132. data/test/models/confirmable_test.rb +398 -0
  133. data/test/models/database_authenticatable_test.rb +207 -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 +158 -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 +9 -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. data/test/test_models.rb +27 -0
  208. metadata +423 -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,276 @@
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
+ array = %w(serializable_hash)
97
+ # to_xml does not call serializable_hash on 3.1
98
+ array << "to_xml" if Rails::VERSION::STRING[0,3] == "3.1"
99
+
100
+ array.each do |method|
101
+ class_eval <<-RUBY, __FILE__, __LINE__
102
+ # Redefine to_xml and serializable_hash in models for more secure defaults.
103
+ # By default, it removes from the serializable model all attributes that
104
+ # are *not* accessible. You can remove this default by using :force_except
105
+ # and passing a new list of attributes you want to exempt. All attributes
106
+ # given to :except will simply add names to exempt to Devise internal list.
107
+ def #{method}(options=nil)
108
+ options ||= {}
109
+ options[:except] = Array(options[:except])
110
+
111
+ if options[:force_except]
112
+ options[:except].concat Array(options[:force_except])
113
+ else
114
+ options[:except].concat BLACKLIST_FOR_SERIALIZATION
115
+ end
116
+ super(options)
117
+ end
118
+ RUBY
119
+ end
120
+
121
+ protected
122
+
123
+ def devise_mailer
124
+ Devise.mailer
125
+ end
126
+
127
+ # This is an internal method called every time Devise needs
128
+ # to send a notification/mail. This can be overriden if you
129
+ # need to customize the e-mail delivery logic. For instance,
130
+ # if you are using a queue to deliver e-mails (delayed job,
131
+ # sidekiq, resque, etc), you must add the delivery to the queue
132
+ # just after the transaction was committed. To achieve this,
133
+ # you can override send_devise_notification to store the
134
+ # deliveries until the after_commit callback is triggered:
135
+ #
136
+ # class User
137
+ # devise :database_authenticatable, :confirmable
138
+ #
139
+ # after_commit :send_pending_notifications
140
+ #
141
+ # protected
142
+ #
143
+ # def send_devise_notification(notification)
144
+ # pending_notifications << notification
145
+ # end
146
+ #
147
+ # def send_pending_notifications
148
+ # pending_notifications.each do |n|
149
+ # devise_mailer.send(n, self).deliver
150
+ # end
151
+ # end
152
+ #
153
+ # def pending_notifications
154
+ # @pending_notifications ||= []
155
+ # end
156
+ # end
157
+ #
158
+ def send_devise_notification(notification, opts={})
159
+ devise_mailer.send(notification, self, opts).deliver
160
+ end
161
+
162
+ def downcase_keys
163
+ self.class.case_insensitive_keys.each { |k| apply_to_attribute_or_variable(k, :downcase!) }
164
+ end
165
+
166
+ def strip_whitespace
167
+ self.class.strip_whitespace_keys.each { |k| apply_to_attribute_or_variable(k, :strip!) }
168
+ end
169
+
170
+ def apply_to_attribute_or_variable(attr, method)
171
+ if self[attr]
172
+ self[attr].try(method)
173
+
174
+ # Use respond_to? here to avoid a regression where globally
175
+ # configured strip_whitespace_keys or case_insensitive_keys were
176
+ # attempting to strip! or downcase! when a model didn't have the
177
+ # globally configured key.
178
+ elsif respond_to?(attr)
179
+ send(attr).try(method)
180
+ end
181
+ end
182
+
183
+ module ClassMethods
184
+ Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys,
185
+ :case_insensitive_keys, :http_authenticatable, :params_authenticatable, :skip_session_storage)
186
+
187
+ def serialize_into_session(record)
188
+ [record.to_key, record.authenticatable_salt]
189
+ end
190
+
191
+ def serialize_from_session(key, salt)
192
+ record = to_adapter.get(key)
193
+ record if record && record.authenticatable_salt == salt
194
+ end
195
+
196
+ def params_authenticatable?(strategy)
197
+ params_authenticatable.is_a?(Array) ?
198
+ params_authenticatable.include?(strategy) : params_authenticatable
199
+ end
200
+
201
+ def http_authenticatable?(strategy)
202
+ http_authenticatable.is_a?(Array) ?
203
+ http_authenticatable.include?(strategy) : http_authenticatable
204
+ end
205
+
206
+ # Find first record based on conditions given (ie by the sign in form).
207
+ # This method is always called during an authentication process but
208
+ # it may be wrapped as well. For instance, database authenticatable
209
+ # provides a `find_for_database_authentication` that wraps a call to
210
+ # this method. This allows you to customize both database authenticatable
211
+ # or the whole authenticate stack by customize `find_for_authentication.`
212
+ #
213
+ # Overwrite to add customized conditions, create a join, or maybe use a
214
+ # namedscope to filter records while authenticating.
215
+ # Example:
216
+ #
217
+ # def self.find_for_authentication(tainted_conditions)
218
+ # find_first_by_auth_conditions(tainted_conditions, :active => true)
219
+ # end
220
+ #
221
+ # Finally, notice that Devise also queries for users in other scenarios
222
+ # besides authentication, for example when retrieving an user to send
223
+ # an e-mail for password reset. In such cases, find_for_authentication
224
+ # is not called.
225
+ def find_for_authentication(tainted_conditions)
226
+ find_first_by_auth_conditions(tainted_conditions)
227
+ end
228
+
229
+ def find_first_by_auth_conditions(tainted_conditions, opts={})
230
+ to_adapter.find_first(devise_param_filter.filter(tainted_conditions).merge(opts))
231
+ end
232
+
233
+ # Find an initialize a record setting an error if it can't be found.
234
+ def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
235
+ find_or_initialize_with_errors([attribute], { attribute => value }, error)
236
+ end
237
+
238
+ # Find an initialize a group of attributes based on a list of required attributes.
239
+ def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
240
+ attributes = attributes.slice(*required_attributes)
241
+ attributes.delete_if { |key, value| value.blank? }
242
+
243
+ if attributes.size == required_attributes.size
244
+ record = find_first_by_auth_conditions(attributes)
245
+ end
246
+
247
+ unless record
248
+ record = new
249
+
250
+ required_attributes.each do |key|
251
+ value = attributes[key]
252
+ record.send("#{key}=", value)
253
+ record.errors.add(key, value.present? ? error : :blank)
254
+ end
255
+ end
256
+
257
+ record
258
+ end
259
+
260
+ protected
261
+
262
+ def devise_param_filter
263
+ @devise_param_filter ||= Devise::ParamFilter.new(case_insensitive_keys, strip_whitespace_keys)
264
+ end
265
+
266
+ # Generate a token by looping and ensuring does not already exist.
267
+ def generate_token(column)
268
+ loop do
269
+ token = Devise.friendly_token
270
+ break token unless to_adapter.find_first({ column => token })
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end