devise-jdguyot 1.2.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 (185) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG.rdoc +532 -0
  3. data/Gemfile +29 -0
  4. data/Gemfile.lock +152 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +353 -0
  7. data/Rakefile +36 -0
  8. data/TODO +4 -0
  9. data/app/controllers/devise/confirmations_controller.rb +33 -0
  10. data/app/controllers/devise/omniauth_callbacks_controller.rb +26 -0
  11. data/app/controllers/devise/passwords_controller.rb +41 -0
  12. data/app/controllers/devise/registrations_controller.rb +110 -0
  13. data/app/controllers/devise/sessions_controller.rb +25 -0
  14. data/app/controllers/devise/unlocks_controller.rb +34 -0
  15. data/app/helpers/devise_helper.rb +19 -0
  16. data/app/mailers/devise/mailer.rb +88 -0
  17. data/app/views/devise/confirmations/new.html.erb +12 -0
  18. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  19. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  20. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  21. data/app/views/devise/passwords/edit.html.erb +16 -0
  22. data/app/views/devise/passwords/new.html.erb +12 -0
  23. data/app/views/devise/registrations/edit.html.erb +25 -0
  24. data/app/views/devise/registrations/new.html.erb +18 -0
  25. data/app/views/devise/sessions/new.html.erb +17 -0
  26. data/app/views/devise/shared/_links.erb +25 -0
  27. data/app/views/devise/unlocks/new.html.erb +12 -0
  28. data/config/locales/en.yml +46 -0
  29. data/devise.gemspec +25 -0
  30. data/lib/devise/controllers/helpers.rb +227 -0
  31. data/lib/devise/controllers/internal_helpers.rb +119 -0
  32. data/lib/devise/controllers/scoped_views.rb +33 -0
  33. data/lib/devise/controllers/url_helpers.rb +39 -0
  34. data/lib/devise/encryptors/authlogic_sha512.rb +19 -0
  35. data/lib/devise/encryptors/base.rb +20 -0
  36. data/lib/devise/encryptors/clearance_sha1.rb +17 -0
  37. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  38. data/lib/devise/encryptors/sha1.rb +25 -0
  39. data/lib/devise/encryptors/sha512.rb +25 -0
  40. data/lib/devise/failure_app.rb +132 -0
  41. data/lib/devise/hooks/activatable.rb +11 -0
  42. data/lib/devise/hooks/forgetable.rb +12 -0
  43. data/lib/devise/hooks/rememberable.rb +48 -0
  44. data/lib/devise/hooks/timeoutable.rb +22 -0
  45. data/lib/devise/hooks/trackable.rb +9 -0
  46. data/lib/devise/mapping.rb +110 -0
  47. data/lib/devise/models/authenticatable.rb +146 -0
  48. data/lib/devise/models/confirmable.rb +160 -0
  49. data/lib/devise/models/database_authenticatable.rb +100 -0
  50. data/lib/devise/models/encryptable.rb +72 -0
  51. data/lib/devise/models/lockable.rb +169 -0
  52. data/lib/devise/models/omniauthable.rb +23 -0
  53. data/lib/devise/models/recoverable.rb +123 -0
  54. data/lib/devise/models/registerable.rb +21 -0
  55. data/lib/devise/models/rememberable.rb +130 -0
  56. data/lib/devise/models/timeoutable.rb +43 -0
  57. data/lib/devise/models/token_authenticatable.rb +72 -0
  58. data/lib/devise/models/trackable.rb +30 -0
  59. data/lib/devise/models/validatable.rb +65 -0
  60. data/lib/devise/models.rb +68 -0
  61. data/lib/devise/modules.rb +30 -0
  62. data/lib/devise/omniauth/config.rb +30 -0
  63. data/lib/devise/omniauth/test_helpers.rb +57 -0
  64. data/lib/devise/omniauth/url_helpers.rb +29 -0
  65. data/lib/devise/omniauth.rb +47 -0
  66. data/lib/devise/orm/active_record.rb +38 -0
  67. data/lib/devise/orm/mongoid.rb +31 -0
  68. data/lib/devise/path_checker.rb +18 -0
  69. data/lib/devise/rails/routes.rb +292 -0
  70. data/lib/devise/rails/warden_compat.rb +125 -0
  71. data/lib/devise/rails.rb +50 -0
  72. data/lib/devise/schema.rb +97 -0
  73. data/lib/devise/strategies/authenticatable.rb +150 -0
  74. data/lib/devise/strategies/base.rb +15 -0
  75. data/lib/devise/strategies/database_authenticatable.rb +21 -0
  76. data/lib/devise/strategies/rememberable.rb +51 -0
  77. data/lib/devise/strategies/token_authenticatable.rb +53 -0
  78. data/lib/devise/test_helpers.rb +100 -0
  79. data/lib/devise/version.rb +3 -0
  80. data/lib/devise.rb +381 -0
  81. data/lib/generators/active_record/devise_generator.rb +28 -0
  82. data/lib/generators/active_record/templates/migration.rb +31 -0
  83. data/lib/generators/devise/devise_generator.rb +17 -0
  84. data/lib/generators/devise/install_generator.rb +24 -0
  85. data/lib/generators/devise/orm_helpers.rb +23 -0
  86. data/lib/generators/devise/views_generator.rb +106 -0
  87. data/lib/generators/mongoid/devise_generator.rb +17 -0
  88. data/lib/generators/templates/README +25 -0
  89. data/lib/generators/templates/devise.rb +186 -0
  90. data/test/controllers/helpers_test.rb +237 -0
  91. data/test/controllers/internal_helpers_test.rb +72 -0
  92. data/test/controllers/url_helpers_test.rb +59 -0
  93. data/test/devise_test.rb +65 -0
  94. data/test/encryptors_test.rb +30 -0
  95. data/test/failure_app_test.rb +187 -0
  96. data/test/generators/active_record_generator_test.rb +24 -0
  97. data/test/generators/install_generator_test.rb +13 -0
  98. data/test/generators/mongoid_generator_test.rb +22 -0
  99. data/test/generators/views_generator_test.rb +35 -0
  100. data/test/indifferent_hash.rb +33 -0
  101. data/test/integration/authenticatable_test.rb +447 -0
  102. data/test/integration/confirmable_test.rb +104 -0
  103. data/test/integration/database_authenticatable_test.rb +60 -0
  104. data/test/integration/http_authenticatable_test.rb +74 -0
  105. data/test/integration/lockable_test.rb +109 -0
  106. data/test/integration/omniauthable_test.rb +107 -0
  107. data/test/integration/recoverable_test.rb +160 -0
  108. data/test/integration/registerable_test.rb +179 -0
  109. data/test/integration/rememberable_test.rb +180 -0
  110. data/test/integration/timeoutable_test.rb +89 -0
  111. data/test/integration/token_authenticatable_test.rb +99 -0
  112. data/test/integration/trackable_test.rb +64 -0
  113. data/test/mailers/confirmation_instructions_test.rb +84 -0
  114. data/test/mailers/reset_password_instructions_test.rb +72 -0
  115. data/test/mailers/unlock_instructions_test.rb +66 -0
  116. data/test/mapping_test.rb +119 -0
  117. data/test/models/confirmable_test.rb +221 -0
  118. data/test/models/database_authenticatable_test.rb +98 -0
  119. data/test/models/encryptable_test.rb +65 -0
  120. data/test/models/lockable_test.rb +204 -0
  121. data/test/models/recoverable_test.rb +190 -0
  122. data/test/models/rememberable_test.rb +279 -0
  123. data/test/models/timeoutable_test.rb +28 -0
  124. data/test/models/token_authenticatable_test.rb +37 -0
  125. data/test/models/trackable_test.rb +5 -0
  126. data/test/models/validatable_test.rb +99 -0
  127. data/test/models_test.rb +84 -0
  128. data/test/omniauth/url_helpers_test.rb +47 -0
  129. data/test/orm/active_record.rb +9 -0
  130. data/test/orm/mongoid.rb +11 -0
  131. data/test/rails_app/Rakefile +10 -0
  132. data/test/rails_app/app/active_record/admin.rb +6 -0
  133. data/test/rails_app/app/active_record/shim.rb +2 -0
  134. data/test/rails_app/app/active_record/user.rb +8 -0
  135. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  136. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  137. data/test/rails_app/app/controllers/application_controller.rb +8 -0
  138. data/test/rails_app/app/controllers/home_controller.rb +16 -0
  139. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  140. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  141. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +7 -0
  142. data/test/rails_app/app/controllers/users_controller.rb +18 -0
  143. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  144. data/test/rails_app/app/mongoid/admin.rb +9 -0
  145. data/test/rails_app/app/mongoid/shim.rb +29 -0
  146. data/test/rails_app/app/mongoid/user.rb +10 -0
  147. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  148. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  149. data/test/rails_app/app/views/home/index.html.erb +1 -0
  150. data/test/rails_app/app/views/home/private.html.erb +1 -0
  151. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  152. data/test/rails_app/app/views/users/index.html.erb +1 -0
  153. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  154. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  155. data/test/rails_app/config/application.rb +40 -0
  156. data/test/rails_app/config/boot.rb +13 -0
  157. data/test/rails_app/config/database.yml +18 -0
  158. data/test/rails_app/config/environment.rb +5 -0
  159. data/test/rails_app/config/environments/development.rb +19 -0
  160. data/test/rails_app/config/environments/production.rb +33 -0
  161. data/test/rails_app/config/environments/test.rb +33 -0
  162. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  163. data/test/rails_app/config/initializers/devise.rb +176 -0
  164. data/test/rails_app/config/initializers/inflections.rb +2 -0
  165. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  166. data/test/rails_app/config/routes.rb +55 -0
  167. data/test/rails_app/config.ru +4 -0
  168. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +31 -0
  169. data/test/rails_app/db/schema.rb +52 -0
  170. data/test/rails_app/lib/shared_admin.rb +9 -0
  171. data/test/rails_app/lib/shared_user.rb +23 -0
  172. data/test/rails_app/public/404.html +26 -0
  173. data/test/rails_app/public/422.html +26 -0
  174. data/test/rails_app/public/500.html +26 -0
  175. data/test/rails_app/public/favicon.ico +0 -0
  176. data/test/rails_app/script/rails +10 -0
  177. data/test/routes_test.rb +179 -0
  178. data/test/support/assertions.rb +24 -0
  179. data/test/support/helpers.rb +60 -0
  180. data/test/support/integration.rb +88 -0
  181. data/test/support/locale/en.yml +4 -0
  182. data/test/support/webrat/integrations/rails.rb +24 -0
  183. data/test/test_helper.rb +29 -0
  184. data/test/test_helpers_test.rb +118 -0
  185. metadata +388 -0
@@ -0,0 +1,169 @@
1
+ module Devise
2
+ module Models
3
+ # Handles blocking a user access after a certain number of attempts.
4
+ # Lockable accepts two different strategies to unlock a user after it's
5
+ # blocked: email and time. The former will send an email to the user when
6
+ # the lock happens, containing a link to unlock it's account. The second
7
+ # will unlock the user automatically after some configured time (ie 2.hours).
8
+ # It's also possible to setup lockable to use both email and time strategies.
9
+ #
10
+ # == Options
11
+ #
12
+ # Lockable adds the following options to devise_for:
13
+ #
14
+ # * +maximum_attempts+: how many attempts should be accepted before blocking the user.
15
+ # * +lock_strategy+: lock the user account by :failed_attempts or :none.
16
+ # * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
17
+ # * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both.
18
+ # * +unlock_keys+: the keys you want to use when locking and unlocking an account
19
+ #
20
+ module Lockable
21
+ extend ActiveSupport::Concern
22
+
23
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
24
+
25
+ # Lock an user setting it's locked_at to actual time.
26
+ def lock_access!
27
+ self.locked_at = Time.now
28
+
29
+ if unlock_strategy_enabled?(:email)
30
+ generate_unlock_token
31
+ send_unlock_instructions
32
+ end
33
+
34
+ save(:validate => false)
35
+ end
36
+
37
+ # Unlock an user by cleaning locket_at and failed_attempts.
38
+ def unlock_access!
39
+ if_access_locked do
40
+ self.locked_at = nil
41
+ self.failed_attempts = 0 if respond_to?(:failed_attempts=)
42
+ self.unlock_token = nil if respond_to?(:unlock_token=)
43
+ save(:validate => false)
44
+ end
45
+ end
46
+
47
+ # Verifies whether a user is locked or not.
48
+ def access_locked?
49
+ locked_at && !lock_expired?
50
+ end
51
+
52
+ # Send unlock instructions by email
53
+ def send_unlock_instructions
54
+ ::Devise.mailer.unlock_instructions(self).deliver
55
+ end
56
+
57
+ # Resend the unlock instructions if the user is locked.
58
+ def resend_unlock_token
59
+ if_access_locked { send_unlock_instructions }
60
+ end
61
+
62
+ # Overwrites active? from Devise::Models::Activatable for locking purposes
63
+ # by verifying whether an user is active to sign in or not based on locked?
64
+ def active?
65
+ super && !access_locked?
66
+ end
67
+
68
+ # Overwrites invalid_message from Devise::Models::Authenticatable to define
69
+ # the correct reason for blocking the sign in.
70
+ def inactive_message
71
+ access_locked? ? :locked : super
72
+ end
73
+
74
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
75
+ # for verifying whether an user is allowed to sign in or not. If the user
76
+ # is locked, it should never be allowed.
77
+ def valid_for_authentication?
78
+ return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
79
+
80
+ case (result = super)
81
+ when Symbol
82
+ return result
83
+ when TrueClass
84
+ self.failed_attempts = 0
85
+ when FalseClass
86
+ # PostgreSQL uses nil as the default value for integer columns set to 0
87
+ self.failed_attempts ||= 0
88
+ self.failed_attempts += 1
89
+ if attempts_exceeded?
90
+ lock_access!
91
+ return :locked
92
+ end
93
+ end
94
+
95
+ save(:validate => false) if changed?
96
+ result
97
+ end
98
+
99
+ protected
100
+
101
+ def attempts_exceeded?
102
+ self.failed_attempts > self.class.maximum_attempts
103
+ end
104
+
105
+ # Generates unlock token
106
+ def generate_unlock_token
107
+ self.unlock_token = self.class.unlock_token
108
+ end
109
+
110
+ # Tells if the lock is expired if :time unlock strategy is active
111
+ def lock_expired?
112
+ if unlock_strategy_enabled?(:time)
113
+ locked_at && locked_at < self.class.unlock_in.ago
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ # Checks whether the record is locked or not, yielding to the block
120
+ # if it's locked, otherwise adds an error to email.
121
+ def if_access_locked
122
+ if access_locked?
123
+ yield
124
+ else
125
+ self.errors.add(:email, :not_locked)
126
+ false
127
+ end
128
+ end
129
+
130
+ module ClassMethods
131
+ # Attempt to find a user by it's email. If a record is found, send new
132
+ # unlock instructions to it. If not user is found, returns a new user
133
+ # with an email not found error.
134
+ # Options must contain the user email
135
+ def send_unlock_instructions(attributes={})
136
+ lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
137
+ lockable.resend_unlock_token if lockable.persisted?
138
+ lockable
139
+ end
140
+
141
+ # Find a user by it's unlock token and try to unlock it.
142
+ # If no user is found, returns a new user with an error.
143
+ # If the user is not locked, creates an error for the user
144
+ # Options must have the unlock_token
145
+ def unlock_access_by_token(unlock_token)
146
+ lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
147
+ lockable.unlock_access! if lockable.persisted?
148
+ lockable
149
+ end
150
+
151
+ # Is the unlock enabled for the given unlock strategy?
152
+ def unlock_strategy_enabled?(strategy)
153
+ [:both, strategy].include?(self.unlock_strategy)
154
+ end
155
+
156
+ # Is the lock enabled for the given lock strategy?
157
+ def lock_strategy_enabled?(strategy)
158
+ self.lock_strategy == strategy
159
+ end
160
+
161
+ def unlock_token
162
+ Devise.friendly_token
163
+ end
164
+
165
+ Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,23 @@
1
+ require 'devise/omniauth'
2
+
3
+ module Devise
4
+ module Models
5
+ # Adds OmniAuth support to your model.
6
+ #
7
+ # == Options
8
+ #
9
+ # Oauthable adds the following options to devise_for:
10
+ #
11
+ # * +omniauth_providers+: Which providers are avaialble to this model. It expects an array:
12
+ #
13
+ # devise_for :database_authenticatable, :omniauthable, :omniauth_providers => [:twitter]
14
+ #
15
+ module Omniauthable
16
+ extend ActiveSupport::Concern
17
+
18
+ module ClassMethods
19
+ Devise::Models.config(self, :omniauth_providers)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,123 @@
1
+ module Devise
2
+ module Models
3
+
4
+ # Recoverable takes care of reseting the user password and send reset instructions.
5
+ #
6
+ # ==Options
7
+ #
8
+ # Recoverable adds the following options to devise_for:
9
+ #
10
+ # * +reset_password_keys+: the keys you want to use when recovering the password for an account
11
+ #
12
+ # == Examples
13
+ #
14
+ # # resets the user password and save the record, true if valid passwords are given, otherwise false
15
+ # User.find(1).reset_password!('password123', 'password123')
16
+ #
17
+ # # only resets the user password, without saving the record
18
+ # user = User.find(1)
19
+ # user.reset_password('password123', 'password123')
20
+ #
21
+ # # creates a new token and send it with instructions about how to reset the password
22
+ # User.find(1).send_reset_password_instructions
23
+ #
24
+ module Recoverable
25
+ extend ActiveSupport::Concern
26
+
27
+ # Update password saving the record and clearing token. Returns true if
28
+ # the passwords are valid and the record was saved, false otherwise.
29
+ def reset_password!(new_password, new_password_confirmation)
30
+ self.password = new_password
31
+ self.password_confirmation = new_password_confirmation
32
+ clear_reset_password_token if valid?
33
+ save
34
+ end
35
+
36
+ # Resets reset password token and send reset password instructions by email
37
+ def send_reset_password_instructions
38
+ generate_reset_password_token! if self.confirmation_token.nil? or !reset_password_period_valid?
39
+ ::Devise.mailer.reset_password_instructions(self).deliver
40
+ end
41
+
42
+ # Checks if the reset password token sent is within the limit time.
43
+ # We do this by calculating if the difference between today and the
44
+ # sending date does not exceed the confirm in time configured.
45
+ # reset_password_within is a model configuration, must always be an integer value.
46
+ #
47
+ # Example:
48
+ #
49
+ # # reset_password_within = 1.day and reset_password_sent_at = today
50
+ # reset_password_period_valid? # returns true
51
+ #
52
+ # # reset_password_within = 5.days and reset_password_sent_at = 4.days.ago
53
+ # reset_password_period_valid? # returns true
54
+ #
55
+ # # reset_password_within = 5.days and reset_password_sent_at = 5.days.ago
56
+ # reset_password_period_valid? # returns false
57
+ #
58
+ # # reset_password_within = 0.days
59
+ # reset_password_period_valid? # will always return false
60
+ #
61
+ def reset_password_period_valid?
62
+ reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
63
+ end
64
+
65
+ protected
66
+
67
+ # Generates a new random token for reset password
68
+ def generate_reset_password_token
69
+ self.reset_password_token = self.class.reset_password_token
70
+ self.reset_password_sent_at = Time.now.utc
71
+ end
72
+
73
+ # Resets the reset password token with and save the record without
74
+ # validating
75
+ def generate_reset_password_token!
76
+ generate_reset_password_token && save(:validate => false)
77
+ end
78
+
79
+ # Removes reset_password token
80
+ def clear_reset_password_token
81
+ self.reset_password_token = nil
82
+ self.reset_password_sent_at = nil
83
+ end
84
+
85
+ module ClassMethods
86
+ # Attempt to find a user by it's email. If a record is found, send new
87
+ # password instructions to it. If not user is found, returns a new user
88
+ # with an email not found error.
89
+ # Attributes must contain the user email
90
+ def send_reset_password_instructions(attributes={})
91
+ recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
92
+ recoverable.send_reset_password_instructions if recoverable.persisted?
93
+ recoverable
94
+ end
95
+
96
+ # Generate a token checking if one does not already exist in the database.
97
+ def reset_password_token
98
+ generate_token(:reset_password_token)
99
+ end
100
+
101
+ # Attempt to find a user by it's reset_password_token to reset its
102
+ # password. If a user is found and token is still valid, reset its password and automatically
103
+ # try saving the record. If not user is found, returns a new user
104
+ # containing an error in reset_password_token attribute.
105
+ # Attributes must contain reset_password_token, password and confirmation
106
+ def reset_password_by_token(attributes={})
107
+ recoverable = find_or_initialize_with_error_by(:reset_password_token, attributes[:reset_password_token])
108
+ if recoverable.persisted?
109
+ if recoverable.reset_password_period_valid?
110
+ recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
111
+ else
112
+ recoverable.errors.add(:reset_password_token, :invalid)
113
+ end
114
+ end
115
+ recoverable
116
+ end
117
+
118
+ Devise::Models.config(self, :reset_password_keys)
119
+ Devise::Models.config(self, :reset_password_within)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,21 @@
1
+ module Devise
2
+ module Models
3
+ # Registerable is responsible for everything related to registering a new
4
+ # resource (ie user sign up).
5
+ module Registerable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ # A convenience method that receives both parameters and session to
10
+ # initialize an user. This can be used by OAuth, for example, to send
11
+ # in the user token and be stored on initialization.
12
+ #
13
+ # By default discards all information sent by the session by calling
14
+ # new with params.
15
+ def new_with_session(params, session)
16
+ new(params)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,130 @@
1
+ require 'devise/strategies/rememberable'
2
+ require 'devise/hooks/rememberable'
3
+ require 'devise/hooks/forgetable'
4
+
5
+ module Devise
6
+ module Models
7
+ # Rememberable manages generating and clearing token for remember the user
8
+ # from a saved cookie. Rememberable also has utility methods for dealing
9
+ # with serializing the user into the cookie and back from the cookie, trying
10
+ # to lookup the record based on the saved information.
11
+ # You probably wouldn't use rememberable methods directly, they are used
12
+ # mostly internally for handling the remember token.
13
+ #
14
+ # == Options
15
+ #
16
+ # Rememberable adds the following options in devise_for:
17
+ #
18
+ # * +remember_for+: the time you want the user will be remembered without
19
+ # asking for credentials. After this time the user will be blocked and
20
+ # will have to enter his credentials again. This configuration is also
21
+ # used to calculate the expires time for the cookie created to remember
22
+ # the user. By default remember_for is 2.weeks.
23
+ #
24
+ # * +remember_across_browsers+: if a valid remember token can be re-used
25
+ # between multiple browsers. By default remember_across_browsers is true
26
+ # and cannot be turned off if you are using password salt instead of remember
27
+ # token.
28
+ #
29
+ # * +extend_remember_period+: if true, extends the user's remember period
30
+ # when remembered via cookie. False by default.
31
+ #
32
+ # * +cookie_options+: configuration options passed to the created cookie.
33
+ #
34
+ # == Examples
35
+ #
36
+ # User.find(1).remember_me! # regenerating the token
37
+ # User.find(1).forget_me! # clearing the token
38
+ #
39
+ # # generating info to put into cookies
40
+ # User.serialize_into_cookie(user)
41
+ #
42
+ # # lookup the user based on the incoming cookie information
43
+ # User.serialize_from_cookie(cookie_string)
44
+ module Rememberable
45
+ extend ActiveSupport::Concern
46
+
47
+ included do
48
+ # Remember me option available in after_authentication hook.
49
+ attr_accessor :remember_me
50
+ end
51
+
52
+ # Generate a new remember token and save the record without validations
53
+ # unless remember_across_browsers is true and the user already has a valid token.
54
+ def remember_me!(extend_period=false)
55
+ self.remember_token = self.class.remember_token if respond_to?(:remember_token) && generate_remember_token?
56
+ self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
57
+ save(:validate => false)
58
+ end
59
+
60
+ # Removes the remember token only if it exists, and save the record
61
+ # without validations.
62
+ def forget_me!
63
+ self.remember_token = nil if respond_to?(:remember_token)
64
+ self.remember_created_at = nil
65
+ save(:validate => false)
66
+ end
67
+
68
+ # Remember token should be expired if expiration time not overpass now.
69
+ def remember_expired?
70
+ remember_created_at.nil? || (remember_expires_at <= Time.now.utc)
71
+ end
72
+
73
+ # Remember token expires at created time + remember_for configuration
74
+ def remember_expires_at
75
+ remember_created_at + self.class.remember_for
76
+ end
77
+
78
+ def rememberable_value
79
+ if respond_to?(:remember_token)
80
+ remember_token
81
+ elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
82
+ salt
83
+ else
84
+ raise "The #{self.class.name} class does not respond to remember_token and " <<
85
+ "authenticatable_salt returns nil. In order to use rememberable, you must " <<
86
+ "add a remember_token field to your model or ensure a password is always set."
87
+ end
88
+ end
89
+
90
+ def cookie_options
91
+ self.class.cookie_options
92
+ end
93
+
94
+ protected
95
+
96
+ # Generate a token unless remember_across_browsers is true and there is
97
+ # an existing remember_token or the existing remember_token has expried.
98
+ def generate_remember_token? #:nodoc:
99
+ !(self.class.remember_across_browsers && remember_token) || remember_expired?
100
+ end
101
+
102
+ # Generate a timestamp if extend_remember_period is true, if no remember_token
103
+ # exists, or if an existing remember token has expired.
104
+ def generate_remember_timestamp?(extend_period) #:nodoc:
105
+ extend_period || remember_created_at.nil? || remember_expired?
106
+ end
107
+
108
+ module ClassMethods
109
+ # Create the cookie key using the record id and remember_token
110
+ def serialize_into_cookie(record)
111
+ [record.to_key, record.rememberable_value]
112
+ end
113
+
114
+ # Recreate the user based on the stored cookie
115
+ def serialize_from_cookie(id, remember_token)
116
+ record = to_adapter.get(id)
117
+ record if record && record.rememberable_value == remember_token && !record.remember_expired?
118
+ end
119
+
120
+ # Generate a token checking if one does not already exist in the database.
121
+ def remember_token
122
+ generate_token(:remember_token)
123
+ end
124
+
125
+ Devise::Models.config(self, :remember_for, :remember_across_browsers,
126
+ :extend_remember_period, :cookie_options)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,43 @@
1
+ require 'devise/hooks/timeoutable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Timeoutable takes care of veryfing whether a user session has already
6
+ # expired or not. When a session expires after the configured time, the user
7
+ # will be asked for credentials again, it means, he/she will be redirected
8
+ # to the sign in page.
9
+ #
10
+ # == Options
11
+ #
12
+ # Timeoutable adds the following options to devise_for:
13
+ #
14
+ # * +timeout_in+: the interval to timeout the user session without activity.
15
+ #
16
+ # == Examples
17
+ #
18
+ # user.timedout?(30.minutes.ago)
19
+ #
20
+ module Timeoutable
21
+ extend ActiveSupport::Concern
22
+
23
+ # Checks whether the user session has expired based on configured time.
24
+ def timedout?(last_access)
25
+ return false if remember_exists_and_not_expired?
26
+
27
+ last_access && last_access <= self.class.timeout_in.ago
28
+ end
29
+
30
+ private
31
+
32
+ def remember_exists_and_not_expired?
33
+ return false unless respond_to?(:remember_expired?)
34
+
35
+ remember_created_at && !remember_expired?
36
+ end
37
+
38
+ module ClassMethods
39
+ Devise::Models.config(self, :timeout_in)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,72 @@
1
+ require 'devise/strategies/token_authenticatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # The TokenAuthenticatable module is responsible for generating an authentication token and
6
+ # validating the authenticity of the same while signing in.
7
+ #
8
+ # This module only provides a few helpers to help you manage the token, but it is up to you
9
+ # to choose how to use it. For example, if you want to have a new token every time the user
10
+ # saves his account, you can do the following:
11
+ #
12
+ # before_save :reset_authentication_token
13
+ #
14
+ # On the other hand, if you want to generate token unless one exists, you should use instead:
15
+ #
16
+ # before_save :ensure_authentication_token
17
+ #
18
+ # If you want to delete the token after it is used, you can do so in the
19
+ # after_token_authentication callback.
20
+ #
21
+ # == Options
22
+ #
23
+ # TokenAuthenticatable adds the following options to devise_for:
24
+ #
25
+ # * +token_authentication_key+: Defines name of the authentication token params key. E.g. /users/sign_in?some_key=...
26
+ #
27
+ # * +stateless_token+: By default, when you sign up with a token, Devise will store the user in session
28
+ # as any other authentication strategy. You can set stateless_token to true to avoid this.
29
+ #
30
+ module TokenAuthenticatable
31
+ extend ActiveSupport::Concern
32
+
33
+ # Generate new authentication token (a.k.a. "single access token").
34
+ def reset_authentication_token
35
+ self.authentication_token = self.class.authentication_token
36
+ end
37
+
38
+ # Generate new authentication token and save the record.
39
+ def reset_authentication_token!
40
+ reset_authentication_token
41
+ save(:validate => false)
42
+ end
43
+
44
+ # Generate authentication token unless already exists.
45
+ def ensure_authentication_token
46
+ reset_authentication_token if authentication_token.blank?
47
+ end
48
+
49
+ # Generate authentication token unless already exists and save the record.
50
+ def ensure_authentication_token!
51
+ reset_authentication_token! if authentication_token.blank?
52
+ end
53
+
54
+ # Hook called after token authentication.
55
+ def after_token_authentication
56
+ end
57
+
58
+ module ClassMethods
59
+ def find_for_token_authentication(conditions)
60
+ find_for_authentication(:authentication_token => conditions[token_authentication_key])
61
+ end
62
+
63
+ # Generate a token checking if one does not already exist in the database.
64
+ def authentication_token
65
+ generate_token(:authentication_token)
66
+ end
67
+
68
+ ::Devise::Models.config(self, :token_authentication_key, :stateless_token)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,30 @@
1
+ require 'devise/hooks/trackable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Track information about your user sign in. It tracks the following columns:
6
+ #
7
+ # * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
8
+ # * current_sign_in_at - A tiemstamp updated when the user signs in
9
+ # * last_sign_in_at - Holds the timestamp of the previous sign in
10
+ # * current_sign_in_ip - The remote ip updated when the user sign in
11
+ # * last_sign_in_at - Holds the remote ip of the previous sign in
12
+ #
13
+ module Trackable
14
+ def update_tracked_fields!(request)
15
+ old_current, new_current = self.current_sign_in_at, Time.now
16
+ self.last_sign_in_at = old_current || new_current
17
+ self.current_sign_in_at = new_current
18
+
19
+ old_current, new_current = self.current_sign_in_ip, request.remote_ip
20
+ self.last_sign_in_ip = old_current || new_current
21
+ self.current_sign_in_ip = new_current
22
+
23
+ self.sign_in_count ||= 0
24
+ self.sign_in_count += 1
25
+
26
+ save(:validate => false)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,65 @@
1
+ module Devise
2
+ module Models
3
+ # Validatable creates all needed validations for a user email and password.
4
+ # It's optional, given you may want to create the validations by yourself.
5
+ # Automatically validate if the email is present, unique and it's format is
6
+ # valid. Also tests presence of password, confirmation and length.
7
+ #
8
+ # == Options
9
+ #
10
+ # Validatable adds the following options to devise_for:
11
+ #
12
+ # * +email_regexp+: the regular expression used to validate e-mails;
13
+ # * +password_length+: a range expressing password length. Defaults to 6..20.
14
+ #
15
+ module Validatable
16
+ # All validations used by this module.
17
+ VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
18
+ :validates_confirmation_of, :validates_length_of ].freeze
19
+
20
+ def self.included(base)
21
+ base.extend ClassMethods
22
+ assert_validations_api!(base)
23
+
24
+ base.class_eval do
25
+ validates_presence_of :email, :if => :email_required?
26
+ validates_uniqueness_of :email, :scope => authentication_keys[1..-1],
27
+ :case_sensitive => case_insensitive_keys.exclude?(:email), :allow_blank => true
28
+ validates_format_of :email, :with => email_regexp, :allow_blank => true
29
+
30
+ with_options :if => :password_required? do |v|
31
+ v.validates_presence_of :password
32
+ v.validates_confirmation_of :password
33
+ v.validates_length_of :password, :within => password_length, :allow_blank => true
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.assert_validations_api!(base) #:nodoc:
39
+ unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
40
+
41
+ unless unavailable_validations.empty?
42
+ raise "Could not use :validatable module since #{base} does not respond " <<
43
+ "to the following methods: #{unavailable_validations.to_sentence}."
44
+ end
45
+ end
46
+
47
+ protected
48
+
49
+ # Checks whether a password is needed or not. For validations only.
50
+ # Passwords are always required if it's a new record, or if the password
51
+ # or confirmation are being set somewhere.
52
+ def password_required?
53
+ !persisted? || !password.nil? || !password_confirmation.nil?
54
+ end
55
+
56
+ def email_required?
57
+ true
58
+ end
59
+
60
+ module ClassMethods
61
+ Devise::Models.config(self, :email_regexp, :password_length)
62
+ end
63
+ end
64
+ end
65
+ end