upstream-devise 2.1.0.rc

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 (215) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +15 -0
  3. data/CHANGELOG.rdoc +846 -0
  4. data/Gemfile +35 -0
  5. data/Gemfile.lock +165 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +384 -0
  8. data/Rakefile +34 -0
  9. data/app/controllers/devise/confirmations_controller.rb +43 -0
  10. data/app/controllers/devise/omniauth_callbacks_controller.rb +24 -0
  11. data/app/controllers/devise/passwords_controller.rb +47 -0
  12. data/app/controllers/devise/registrations_controller.rb +107 -0
  13. data/app/controllers/devise/sessions_controller.rb +49 -0
  14. data/app/controllers/devise/unlocks_controller.rb +44 -0
  15. data/app/controllers/devise_controller.rb +177 -0
  16. data/app/helpers/devise_helper.rb +25 -0
  17. data/app/mailers/devise/mailer.rb +15 -0
  18. data/app/views/devise/_links.erb +3 -0
  19. data/app/views/devise/confirmations/new.html.erb +12 -0
  20. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  21. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  22. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  23. data/app/views/devise/passwords/edit.html.erb +16 -0
  24. data/app/views/devise/passwords/new.html.erb +12 -0
  25. data/app/views/devise/registrations/edit.html.erb +25 -0
  26. data/app/views/devise/registrations/new.html.erb +18 -0
  27. data/app/views/devise/sessions/new.html.erb +17 -0
  28. data/app/views/devise/shared/_links.erb +25 -0
  29. data/app/views/devise/unlocks/new.html.erb +12 -0
  30. data/config/locales/en.yml +57 -0
  31. data/devise.gemspec +25 -0
  32. data/gemfiles/Gemfile.rails-3.1.x +35 -0
  33. data/gemfiles/Gemfile.rails-3.1.x.lock +167 -0
  34. data/lib/devise.rb +455 -0
  35. data/lib/devise/controllers/helpers.rb +269 -0
  36. data/lib/devise/controllers/rememberable.rb +52 -0
  37. data/lib/devise/controllers/scoped_views.rb +17 -0
  38. data/lib/devise/controllers/url_helpers.rb +67 -0
  39. data/lib/devise/delegator.rb +17 -0
  40. data/lib/devise/encryptors/authlogic_sha512.rb +19 -0
  41. data/lib/devise/encryptors/base.rb +24 -0
  42. data/lib/devise/encryptors/clearance_sha1.rb +17 -0
  43. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  44. data/lib/devise/encryptors/sha1.rb +25 -0
  45. data/lib/devise/encryptors/sha512.rb +25 -0
  46. data/lib/devise/failure_app.rb +185 -0
  47. data/lib/devise/hooks/activatable.rb +11 -0
  48. data/lib/devise/hooks/forgetable.rb +9 -0
  49. data/lib/devise/hooks/lockable.rb +7 -0
  50. data/lib/devise/hooks/rememberable.rb +6 -0
  51. data/lib/devise/hooks/timeoutable.rb +22 -0
  52. data/lib/devise/hooks/trackable.rb +9 -0
  53. data/lib/devise/mailers/helpers.rb +86 -0
  54. data/lib/devise/mapping.rb +172 -0
  55. data/lib/devise/models.rb +123 -0
  56. data/lib/devise/models/authenticatable.rb +231 -0
  57. data/lib/devise/models/confirmable.rb +242 -0
  58. data/lib/devise/models/database_authenticatable.rb +126 -0
  59. data/lib/devise/models/encryptable.rb +86 -0
  60. data/lib/devise/models/lockable.rb +185 -0
  61. data/lib/devise/models/omniauthable.rb +27 -0
  62. data/lib/devise/models/recoverable.rb +140 -0
  63. data/lib/devise/models/registerable.rb +25 -0
  64. data/lib/devise/models/rememberable.rb +125 -0
  65. data/lib/devise/models/timeoutable.rb +49 -0
  66. data/lib/devise/models/token_authenticatable.rb +77 -0
  67. data/lib/devise/models/trackable.rb +35 -0
  68. data/lib/devise/models/validatable.rb +66 -0
  69. data/lib/devise/modules.rb +30 -0
  70. data/lib/devise/omniauth.rb +28 -0
  71. data/lib/devise/omniauth/config.rb +45 -0
  72. data/lib/devise/omniauth/url_helpers.rb +33 -0
  73. data/lib/devise/orm/active_record.rb +3 -0
  74. data/lib/devise/orm/mongoid.rb +3 -0
  75. data/lib/devise/param_filter.rb +41 -0
  76. data/lib/devise/rails.rb +54 -0
  77. data/lib/devise/rails/routes.rb +412 -0
  78. data/lib/devise/rails/warden_compat.rb +43 -0
  79. data/lib/devise/strategies/authenticatable.rb +165 -0
  80. data/lib/devise/strategies/base.rb +15 -0
  81. data/lib/devise/strategies/database_authenticatable.rb +21 -0
  82. data/lib/devise/strategies/rememberable.rb +53 -0
  83. data/lib/devise/strategies/token_authenticatable.rb +57 -0
  84. data/lib/devise/test_helpers.rb +130 -0
  85. data/lib/devise/version.rb +3 -0
  86. data/lib/generators/active_record/devise_generator.rb +78 -0
  87. data/lib/generators/active_record/templates/migration.rb +19 -0
  88. data/lib/generators/active_record/templates/migration_existing.rb +26 -0
  89. data/lib/generators/devise/devise_generator.rb +24 -0
  90. data/lib/generators/devise/install_generator.rb +24 -0
  91. data/lib/generators/devise/orm_helpers.rb +32 -0
  92. data/lib/generators/devise/views_generator.rb +110 -0
  93. data/lib/generators/mongoid/devise_generator.rb +60 -0
  94. data/lib/generators/templates/README +31 -0
  95. data/lib/generators/templates/devise.rb +216 -0
  96. data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  97. data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  98. data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
  99. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +15 -0
  100. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
  101. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
  102. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +22 -0
  103. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
  104. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
  105. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +15 -0
  106. data/test/controllers/custom_strategy_test.rb +62 -0
  107. data/test/controllers/helpers_test.rb +254 -0
  108. data/test/controllers/internal_helpers_test.rb +97 -0
  109. data/test/controllers/sessions_controller_test.rb +36 -0
  110. data/test/controllers/url_helpers_test.rb +59 -0
  111. data/test/delegator_test.rb +19 -0
  112. data/test/devise_test.rb +72 -0
  113. data/test/encryptors_test.rb +30 -0
  114. data/test/failure_app_test.rb +211 -0
  115. data/test/generators/active_record_generator_test.rb +69 -0
  116. data/test/generators/devise_generator_test.rb +39 -0
  117. data/test/generators/install_generator_test.rb +13 -0
  118. data/test/generators/mongoid_generator_test.rb +23 -0
  119. data/test/generators/views_generator_test.rb +52 -0
  120. data/test/helpers/devise_helper_test.rb +51 -0
  121. data/test/indifferent_hash.rb +33 -0
  122. data/test/integration/authenticatable_test.rb +587 -0
  123. data/test/integration/confirmable_test.rb +255 -0
  124. data/test/integration/database_authenticatable_test.rb +82 -0
  125. data/test/integration/http_authenticatable_test.rb +97 -0
  126. data/test/integration/lockable_test.rb +224 -0
  127. data/test/integration/omniauthable_test.rb +133 -0
  128. data/test/integration/recoverable_test.rb +300 -0
  129. data/test/integration/registerable_test.rb +324 -0
  130. data/test/integration/rememberable_test.rb +158 -0
  131. data/test/integration/timeoutable_test.rb +114 -0
  132. data/test/integration/token_authenticatable_test.rb +161 -0
  133. data/test/integration/trackable_test.rb +92 -0
  134. data/test/mailers/confirmation_instructions_test.rb +95 -0
  135. data/test/mailers/reset_password_instructions_test.rb +83 -0
  136. data/test/mailers/unlock_instructions_test.rb +77 -0
  137. data/test/mapping_test.rb +127 -0
  138. data/test/models/authenticatable_test.rb +7 -0
  139. data/test/models/confirmable_test.rb +357 -0
  140. data/test/models/database_authenticatable_test.rb +189 -0
  141. data/test/models/encryptable_test.rb +73 -0
  142. data/test/models/lockable_test.rb +263 -0
  143. data/test/models/omniauthable_test.rb +7 -0
  144. data/test/models/recoverable_test.rb +205 -0
  145. data/test/models/registerable_test.rb +7 -0
  146. data/test/models/rememberable_test.rb +174 -0
  147. data/test/models/serializable_test.rb +48 -0
  148. data/test/models/timeoutable_test.rb +46 -0
  149. data/test/models/token_authenticatable_test.rb +55 -0
  150. data/test/models/trackable_test.rb +13 -0
  151. data/test/models/validatable_test.rb +117 -0
  152. data/test/models_test.rb +179 -0
  153. data/test/omniauth/config_test.rb +57 -0
  154. data/test/omniauth/url_helpers_test.rb +58 -0
  155. data/test/orm/active_record.rb +9 -0
  156. data/test/orm/mongoid.rb +14 -0
  157. data/test/rails_app/Rakefile +10 -0
  158. data/test/rails_app/app/active_record/admin.rb +6 -0
  159. data/test/rails_app/app/active_record/shim.rb +2 -0
  160. data/test/rails_app/app/active_record/user.rb +6 -0
  161. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  162. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  163. data/test/rails_app/app/controllers/application_controller.rb +8 -0
  164. data/test/rails_app/app/controllers/home_controller.rb +25 -0
  165. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  166. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  167. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
  168. data/test/rails_app/app/controllers/users_controller.rb +23 -0
  169. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  170. data/test/rails_app/app/mailers/users/mailer.rb +3 -0
  171. data/test/rails_app/app/mongoid/admin.rb +30 -0
  172. data/test/rails_app/app/mongoid/shim.rb +24 -0
  173. data/test/rails_app/app/mongoid/user.rb +45 -0
  174. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  175. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  176. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
  177. data/test/rails_app/app/views/home/index.html.erb +1 -0
  178. data/test/rails_app/app/views/home/join.html.erb +1 -0
  179. data/test/rails_app/app/views/home/private.html.erb +1 -0
  180. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
  181. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  182. data/test/rails_app/app/views/users/index.html.erb +1 -0
  183. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  184. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  185. data/test/rails_app/config.ru +4 -0
  186. data/test/rails_app/config/application.rb +41 -0
  187. data/test/rails_app/config/boot.rb +8 -0
  188. data/test/rails_app/config/database.yml +18 -0
  189. data/test/rails_app/config/environment.rb +5 -0
  190. data/test/rails_app/config/environments/development.rb +18 -0
  191. data/test/rails_app/config/environments/production.rb +33 -0
  192. data/test/rails_app/config/environments/test.rb +33 -0
  193. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  194. data/test/rails_app/config/initializers/devise.rb +186 -0
  195. data/test/rails_app/config/initializers/inflections.rb +2 -0
  196. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  197. data/test/rails_app/config/routes.rb +90 -0
  198. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +77 -0
  199. data/test/rails_app/db/schema.rb +52 -0
  200. data/test/rails_app/lib/shared_admin.rb +14 -0
  201. data/test/rails_app/lib/shared_user.rb +26 -0
  202. data/test/rails_app/public/404.html +26 -0
  203. data/test/rails_app/public/422.html +26 -0
  204. data/test/rails_app/public/500.html +26 -0
  205. data/test/rails_app/public/favicon.ico +0 -0
  206. data/test/rails_app/script/rails +10 -0
  207. data/test/routes_test.rb +248 -0
  208. data/test/support/assertions.rb +42 -0
  209. data/test/support/helpers.rb +91 -0
  210. data/test/support/integration.rb +90 -0
  211. data/test/support/locale/en.yml +4 -0
  212. data/test/support/webrat/integrations/rails.rb +24 -0
  213. data/test/test_helper.rb +27 -0
  214. data/test/test_helpers_test.rb +134 -0
  215. metadata +451 -0
@@ -0,0 +1,123 @@
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; self; end).send :attr_accessor, :available_configs
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
+
55
+ klass.devise_modules.each do |mod|
56
+ instance = klass.new
57
+
58
+ if const_get(mod.to_s.classify).respond_to?(:required_fields)
59
+ const_get(mod.to_s.classify).required_fields(klass).each do |field|
60
+ failed_attributes << field unless instance.respond_to?(field)
61
+ end
62
+ else
63
+ ActiveSupport::Deprecation.warn "The module #{mod} doesn't implement self.required_fields(klass). " \
64
+ "Devise uses required_fields to warn developers of any missing fields in their models. " \
65
+ "Please implement #{mod}.required_fields(klass) that returns an array of symbols with the required fields."
66
+ end
67
+ end
68
+
69
+ if failed_attributes.any?
70
+ fail Devise::Models::MissingAttribute.new(failed_attributes)
71
+ end
72
+ end
73
+
74
+ # Include the chosen devise modules in your model:
75
+ #
76
+ # devise :database_authenticatable, :confirmable, :recoverable
77
+ #
78
+ # You can also give any of the devise configuration values in form of a hash,
79
+ # with specific values for this model. Please check your Devise initializer
80
+ # for a complete description on those values.
81
+ #
82
+ def devise(*modules)
83
+ options = modules.extract_options!.dup
84
+
85
+ selected_modules = modules.map(&:to_sym).uniq.sort_by do |s|
86
+ Devise::ALL.index(s) || -1 # follow Devise::ALL order
87
+ end
88
+
89
+ devise_modules_hook! do
90
+ include Devise::Models::Authenticatable
91
+ selected_modules.each do |m|
92
+ mod = Devise::Models.const_get(m.to_s.classify)
93
+
94
+ if mod.const_defined?("ClassMethods")
95
+ class_mod = mod.const_get("ClassMethods")
96
+ extend class_mod
97
+
98
+ if class_mod.respond_to?(:available_configs)
99
+ available_configs = class_mod.available_configs
100
+ available_configs.each do |config|
101
+ next unless options.key?(config)
102
+ send(:"#{config}=", options.delete(config))
103
+ end
104
+ end
105
+ end
106
+
107
+ include mod
108
+ end
109
+
110
+ self.devise_modules |= selected_modules
111
+ options.each { |key, value| send(:"#{key}=", value) }
112
+ end
113
+ end
114
+
115
+ # The hook which is called inside devise.
116
+ # So your ORM can include devise compatibility stuff.
117
+ def devise_modules_hook!
118
+ yield
119
+ end
120
+ end
121
+ end
122
+
123
+ require 'devise/models/authenticatable'
@@ -0,0 +1,231 @@
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 devise_mailer
97
+ Devise.mailer
98
+ end
99
+
100
+ def headers_for(name)
101
+ {}
102
+ end
103
+
104
+ def downcase_keys
105
+ self.class.case_insensitive_keys.each { |k| self[k].try(:downcase!) }
106
+ end
107
+
108
+ def strip_whitespace
109
+ self.class.strip_whitespace_keys.each { |k| self[k].try(:strip!) }
110
+ end
111
+
112
+ array = %w(serializable_hash)
113
+ # to_xml does not call serializable_hash on 3.1
114
+ array << "to_xml" if Rails::VERSION::STRING[0,3] == "3.1"
115
+
116
+ array.each do |method|
117
+ class_eval <<-RUBY, __FILE__, __LINE__
118
+ # Redefine to_xml and serializable_hash in models for more secure defaults.
119
+ # By default, it removes from the serializable model all attributes that
120
+ # are *not* accessible. You can remove this default by using :force_except
121
+ # and passing a new list of attributes you want to exempt. All attributes
122
+ # given to :except will simply add names to exempt to Devise internal list.
123
+ def #{method}(options=nil)
124
+ options ||= {}
125
+ options[:except] = Array(options[:except])
126
+
127
+ if options[:force_except]
128
+ options[:except].concat Array(options[:force_except])
129
+ else
130
+ options[:except].concat BLACKLIST_FOR_SERIALIZATION
131
+ end
132
+ super(options)
133
+ end
134
+ RUBY
135
+ end
136
+
137
+ module ClassMethods
138
+ Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys,
139
+ :case_insensitive_keys, :http_authenticatable, :params_authenticatable, :skip_session_storage)
140
+
141
+ def serialize_into_session(record)
142
+ [record.to_key, record.authenticatable_salt]
143
+ end
144
+
145
+ def serialize_from_session(key, salt)
146
+ record = to_adapter.get(key)
147
+ record if record && record.authenticatable_salt == salt
148
+ end
149
+
150
+ def params_authenticatable?(strategy)
151
+ params_authenticatable.is_a?(Array) ?
152
+ params_authenticatable.include?(strategy) : params_authenticatable
153
+ end
154
+
155
+ def http_authenticatable?(strategy)
156
+ http_authenticatable.is_a?(Array) ?
157
+ http_authenticatable.include?(strategy) : http_authenticatable
158
+ end
159
+
160
+ # Find first record based on conditions given (ie by the sign in form).
161
+ # This method is always called during an authentication process but
162
+ # it may be wrapped as well. For instance, database authenticatable
163
+ # provides a `find_for_database_authentication` that wraps a call to
164
+ # this method. This allows you to customize both database authenticatable
165
+ # or the whole authenticate stack by customize `find_for_authentication.`
166
+ #
167
+ # Overwrite to add customized conditions, create a join, or maybe use a
168
+ # namedscope to filter records while authenticating.
169
+ # Example:
170
+ #
171
+ # def self.find_for_authentication(conditions={})
172
+ # conditions[:active] = true
173
+ # super
174
+ # end
175
+ #
176
+ # Finally, notice that Devise also queries for users in other scenarios
177
+ # besides authentication, for example when retrieving an user to send
178
+ # an e-mail for password reset. In such cases, find_for_authentication
179
+ # is not called.
180
+ def find_for_authentication(conditions)
181
+ find_first_by_auth_conditions(conditions)
182
+ end
183
+
184
+ def find_first_by_auth_conditions(conditions)
185
+ to_adapter.find_first devise_param_filter.filter(conditions)
186
+ end
187
+
188
+ # Find an initialize a record setting an error if it can't be found.
189
+ def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
190
+ find_or_initialize_with_errors([attribute], { attribute => value }, error)
191
+ end
192
+
193
+ # Find an initialize a group of attributes based on a list of required attributes.
194
+ def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
195
+ attributes = attributes.slice(*required_attributes)
196
+ attributes.delete_if { |key, value| value.blank? }
197
+
198
+ if attributes.size == required_attributes.size
199
+ record = find_first_by_auth_conditions(attributes)
200
+ end
201
+
202
+ unless record
203
+ record = new
204
+
205
+ required_attributes.each do |key|
206
+ value = attributes[key]
207
+ record.send("#{key}=", value)
208
+ record.errors.add(key, value.present? ? error : :blank)
209
+ end
210
+ end
211
+
212
+ record
213
+ end
214
+
215
+ protected
216
+
217
+ def devise_param_filter
218
+ @devise_param_filter ||= Devise::ParamFilter.new(case_insensitive_keys, strip_whitespace_keys)
219
+ end
220
+
221
+ # Generate a token by looping and ensuring does not already exist.
222
+ def generate_token(column)
223
+ loop do
224
+ token = Devise.friendly_token
225
+ break token unless to_adapter.find_first({ column => token })
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,242 @@
1
+ module Devise
2
+ module Models
3
+ # Confirmable is responsible to verify if an account is already confirmed to
4
+ # sign in, and to send emails with confirmation instructions.
5
+ # Confirmation instructions are sent to the user email after creating a
6
+ # record and when manually requested by a new confirmation instruction request.
7
+ #
8
+ # == Options
9
+ #
10
+ # Confirmable adds the following options to devise_for:
11
+ #
12
+ # * +allow_unconfirmed_access_for+: the time you want to allow the user to access his account
13
+ # before confirming it. After this period, the user access is denied. You can
14
+ # use this to let your user access some features of your application without
15
+ # confirming the account, but blocking it after a certain period (ie 7 days).
16
+ # By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
17
+ # * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
18
+ # initial account confirmation) to be applied. Requires additional unconfirmed_email
19
+ # db field to be setup (t.reconfirmable in migrations). Until confirmed new email is
20
+ # stored in unconfirmed email column, and copied to email column on successful
21
+ # confirmation.
22
+ #
23
+ # == Examples
24
+ #
25
+ # User.find(1).confirm! # returns true unless it's already confirmed
26
+ # User.find(1).confirmed? # true/false
27
+ # User.find(1).send_confirmation_instructions # manually send instructions
28
+ #
29
+ module Confirmable
30
+ extend ActiveSupport::Concern
31
+
32
+ included do
33
+ before_create :generate_confirmation_token, :if => :confirmation_required?
34
+ after_create :send_on_create_confirmation_instructions, :if => :confirmation_required?
35
+ before_update :postpone_email_change_until_confirmation, :if => :postpone_email_change?
36
+ after_update :send_confirmation_instructions, :if => :reconfirmation_required?
37
+ end
38
+
39
+ def self.required_fields(klass)
40
+ required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]
41
+ required_methods << :unconfirmed_email if klass.reconfirmable
42
+ required_methods
43
+ end
44
+
45
+ # Confirm a user by setting it's confirmed_at to actual time. If the user
46
+ # is already confirmed, add an error to email field. If the user is invalid
47
+ # add errors
48
+ def confirm!
49
+ pending_any_confirmation do
50
+ self.confirmation_token = nil
51
+ self.confirmed_at = Time.now.utc
52
+
53
+ if self.class.reconfirmable && unconfirmed_email.present?
54
+ skip_reconfirmation!
55
+ self.email = unconfirmed_email
56
+ self.unconfirmed_email = nil
57
+
58
+ # We need to validate in such cases to enforce e-mail uniqueness
59
+ save(:validate => true)
60
+ else
61
+ save(:validate => false)
62
+ end
63
+ end
64
+ end
65
+
66
+ # Verifies whether a user is confirmed or not
67
+ def confirmed?
68
+ !!confirmed_at
69
+ end
70
+
71
+ def pending_reconfirmation?
72
+ self.class.reconfirmable && unconfirmed_email.present?
73
+ end
74
+
75
+ # Send confirmation instructions by email
76
+ def send_confirmation_instructions
77
+ self.confirmation_token = nil if reconfirmation_required?
78
+ @reconfirmation_required = false
79
+
80
+ generate_confirmation_token! if self.confirmation_token.blank?
81
+ self.devise_mailer.confirmation_instructions(self).deliver
82
+ end
83
+
84
+ # Resend confirmation token. This method does not need to generate a new token.
85
+ def resend_confirmation_token
86
+ pending_any_confirmation { send_confirmation_instructions }
87
+ end
88
+
89
+ # Overwrites active_for_authentication? for confirmation
90
+ # by verifying whether a user is active to sign in or not. If the user
91
+ # is already confirmed, it should never be blocked. Otherwise we need to
92
+ # calculate if the confirm time has not expired for this user.
93
+ def active_for_authentication?
94
+ super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
95
+ end
96
+
97
+ # The message to be shown if the account is inactive.
98
+ def inactive_message
99
+ !confirmed? ? :unconfirmed : super
100
+ end
101
+
102
+ # If you don't want confirmation to be sent on create, neither a code
103
+ # to be generated, call skip_confirmation!
104
+ def skip_confirmation!
105
+ self.confirmed_at = Time.now.utc
106
+ end
107
+
108
+ # If you don't want reconfirmation to be sent, neither a code
109
+ # to be generated, call skip_reconfirmation!
110
+ def skip_reconfirmation!
111
+ @bypass_postpone = true
112
+ end
113
+
114
+ def headers_for(action)
115
+ headers = super
116
+ if action == :confirmation_instructions && pending_reconfirmation?
117
+ headers[:to] = unconfirmed_email
118
+ end
119
+ headers
120
+ end
121
+
122
+ protected
123
+
124
+ # A callback method used to deliver confirmation
125
+ # instructions on creation. This can be overriden
126
+ # in models to map to a nice sign up e-mail.
127
+ def send_on_create_confirmation_instructions
128
+ self.devise_mailer.confirmation_instructions(self).deliver
129
+ end
130
+
131
+ # Callback to overwrite if confirmation is required or not.
132
+ def confirmation_required?
133
+ !confirmed?
134
+ end
135
+
136
+ # Checks if the confirmation for the user is within the limit time.
137
+ # We do this by calculating if the difference between today and the
138
+ # confirmation sent date does not exceed the confirm in time configured.
139
+ # Confirm_within is a model configuration, must always be an integer value.
140
+ #
141
+ # Example:
142
+ #
143
+ # # allow_unconfirmed_access_for = 1.day and confirmation_sent_at = today
144
+ # confirmation_period_valid? # returns true
145
+ #
146
+ # # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 4.days.ago
147
+ # confirmation_period_valid? # returns true
148
+ #
149
+ # # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 5.days.ago
150
+ # confirmation_period_valid? # returns false
151
+ #
152
+ # # allow_unconfirmed_access_for = 0.days
153
+ # confirmation_period_valid? # will always return false
154
+ #
155
+ def confirmation_period_valid?
156
+ confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago
157
+ end
158
+
159
+ # Checks whether the record requires any confirmation.
160
+ def pending_any_confirmation
161
+ if !confirmed? || pending_reconfirmation?
162
+ yield
163
+ else
164
+ self.errors.add(:email, :already_confirmed)
165
+ false
166
+ end
167
+ end
168
+
169
+ # Generates a new random token for confirmation, and stores the time
170
+ # this token is being generated
171
+ def generate_confirmation_token
172
+ self.confirmation_token = self.class.confirmation_token
173
+ self.confirmation_sent_at = Time.now.utc
174
+ end
175
+
176
+ def generate_confirmation_token!
177
+ generate_confirmation_token && save(:validate => false)
178
+ end
179
+
180
+ def after_password_reset
181
+ super
182
+ confirm! unless confirmed?
183
+ end
184
+
185
+ def postpone_email_change_until_confirmation
186
+ @reconfirmation_required = true
187
+ self.unconfirmed_email = self.email
188
+ self.email = self.email_was
189
+ end
190
+
191
+ def postpone_email_change?
192
+ postpone = self.class.reconfirmable && email_changed? && !@bypass_postpone
193
+ @bypass_postpone = nil
194
+ postpone
195
+ end
196
+
197
+ def reconfirmation_required?
198
+ self.class.reconfirmable && @reconfirmation_required
199
+ end
200
+
201
+ module ClassMethods
202
+ # Attempt to find a user by its email. If a record is found, send new
203
+ # confirmation instructions to it. If not, try searching for a user by unconfirmed_email
204
+ # field. If no user is found, returns a new user with an email not found error.
205
+ # Options must contain the user email
206
+ def send_confirmation_instructions(attributes={})
207
+ confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
208
+ unless confirmable.try(:persisted?)
209
+ confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
210
+ end
211
+ confirmable.resend_confirmation_token if confirmable.persisted?
212
+ confirmable
213
+ end
214
+
215
+ # Find a user by its confirmation token and try to confirm it.
216
+ # If no user is found, returns a new user with an error.
217
+ # If the user is already confirmed, create an error for the user
218
+ # Options must have the confirmation_token
219
+ def confirm_by_token(confirmation_token)
220
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
221
+ confirmable.confirm! if confirmable.persisted?
222
+ confirmable
223
+ end
224
+
225
+ # Generate a token checking if one does not already exist in the database.
226
+ def confirmation_token
227
+ generate_token(:confirmation_token)
228
+ end
229
+
230
+ # Find a record for confirmation by unconfirmed email field
231
+ def find_by_unconfirmed_email_with_errors(attributes = {})
232
+ unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
233
+ unconfirmed_attributes = attributes.symbolize_keys
234
+ unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
235
+ find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
236
+ end
237
+
238
+ Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable)
239
+ end
240
+ end
241
+ end
242
+ end