devise-edge 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 (161) hide show
  1. data/CHANGELOG.rdoc +500 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +335 -0
  4. data/app/controllers/devise/confirmations_controller.rb +33 -0
  5. data/app/controllers/devise/oauth_callbacks_controller.rb +4 -0
  6. data/app/controllers/devise/passwords_controller.rb +41 -0
  7. data/app/controllers/devise/registrations_controller.rb +75 -0
  8. data/app/controllers/devise/sessions_controller.rb +23 -0
  9. data/app/controllers/devise/unlocks_controller.rb +34 -0
  10. data/app/helpers/devise_helper.rb +17 -0
  11. data/app/mailers/devise/mailer.rb +88 -0
  12. data/app/views/devise/confirmations/new.html.erb +12 -0
  13. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  14. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  15. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  16. data/app/views/devise/passwords/edit.html.erb +16 -0
  17. data/app/views/devise/passwords/new.html.erb +12 -0
  18. data/app/views/devise/registrations/edit.html.erb +25 -0
  19. data/app/views/devise/registrations/new.html.erb +18 -0
  20. data/app/views/devise/sessions/new.html.erb +17 -0
  21. data/app/views/devise/shared/_links.erb +25 -0
  22. data/app/views/devise/unlocks/new.html.erb +12 -0
  23. data/config/locales/en.yml +42 -0
  24. data/lib/devise.rb +371 -0
  25. data/lib/devise/controllers/helpers.rb +261 -0
  26. data/lib/devise/controllers/internal_helpers.rb +113 -0
  27. data/lib/devise/controllers/scoped_views.rb +33 -0
  28. data/lib/devise/controllers/url_helpers.rb +39 -0
  29. data/lib/devise/encryptors/authlogic_sha512.rb +19 -0
  30. data/lib/devise/encryptors/base.rb +20 -0
  31. data/lib/devise/encryptors/clearance_sha1.rb +17 -0
  32. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  33. data/lib/devise/encryptors/sha1.rb +25 -0
  34. data/lib/devise/encryptors/sha512.rb +25 -0
  35. data/lib/devise/failure_app.rb +126 -0
  36. data/lib/devise/hooks/activatable.rb +11 -0
  37. data/lib/devise/hooks/forgetable.rb +12 -0
  38. data/lib/devise/hooks/rememberable.rb +45 -0
  39. data/lib/devise/hooks/timeoutable.rb +22 -0
  40. data/lib/devise/hooks/trackable.rb +9 -0
  41. data/lib/devise/mapping.rb +105 -0
  42. data/lib/devise/models.rb +66 -0
  43. data/lib/devise/models/authenticatable.rb +143 -0
  44. data/lib/devise/models/confirmable.rb +160 -0
  45. data/lib/devise/models/database_authenticatable.rb +94 -0
  46. data/lib/devise/models/encryptable.rb +65 -0
  47. data/lib/devise/models/lockable.rb +168 -0
  48. data/lib/devise/models/oauthable.rb +49 -0
  49. data/lib/devise/models/recoverable.rb +83 -0
  50. data/lib/devise/models/registerable.rb +21 -0
  51. data/lib/devise/models/rememberable.rb +122 -0
  52. data/lib/devise/models/timeoutable.rb +33 -0
  53. data/lib/devise/models/token_authenticatable.rb +72 -0
  54. data/lib/devise/models/trackable.rb +30 -0
  55. data/lib/devise/models/validatable.rb +60 -0
  56. data/lib/devise/modules.rb +30 -0
  57. data/lib/devise/oauth.rb +41 -0
  58. data/lib/devise/oauth/config.rb +33 -0
  59. data/lib/devise/oauth/helpers.rb +18 -0
  60. data/lib/devise/oauth/internal_helpers.rb +182 -0
  61. data/lib/devise/oauth/test_helpers.rb +29 -0
  62. data/lib/devise/oauth/url_helpers.rb +35 -0
  63. data/lib/devise/orm/active_record.rb +36 -0
  64. data/lib/devise/orm/mongo_mapper.rb +46 -0
  65. data/lib/devise/orm/mongoid.rb +29 -0
  66. data/lib/devise/path_checker.rb +18 -0
  67. data/lib/devise/rails.rb +67 -0
  68. data/lib/devise/rails/routes.rb +260 -0
  69. data/lib/devise/rails/warden_compat.rb +42 -0
  70. data/lib/devise/schema.rb +96 -0
  71. data/lib/devise/strategies/authenticatable.rb +150 -0
  72. data/lib/devise/strategies/base.rb +15 -0
  73. data/lib/devise/strategies/database_authenticatable.rb +21 -0
  74. data/lib/devise/strategies/rememberable.rb +51 -0
  75. data/lib/devise/strategies/token_authenticatable.rb +53 -0
  76. data/lib/devise/test_helpers.rb +100 -0
  77. data/lib/devise/version.rb +3 -0
  78. data/lib/generators/active_record/devise_generator.rb +28 -0
  79. data/lib/generators/active_record/templates/migration.rb +30 -0
  80. data/lib/generators/devise/devise_generator.rb +17 -0
  81. data/lib/generators/devise/install_generator.rb +24 -0
  82. data/lib/generators/devise/orm_helpers.rb +24 -0
  83. data/lib/generators/devise/views_generator.rb +63 -0
  84. data/lib/generators/mongoid/devise_generator.rb +17 -0
  85. data/lib/generators/templates/README +25 -0
  86. data/lib/generators/templates/devise.rb +168 -0
  87. data/test/controllers/helpers_test.rb +220 -0
  88. data/test/controllers/internal_helpers_test.rb +56 -0
  89. data/test/controllers/url_helpers_test.rb +59 -0
  90. data/test/devise_test.rb +65 -0
  91. data/test/encryptors_test.rb +30 -0
  92. data/test/failure_app_test.rb +148 -0
  93. data/test/integration/authenticatable_test.rb +424 -0
  94. data/test/integration/confirmable_test.rb +104 -0
  95. data/test/integration/database_authenticatable_test.rb +38 -0
  96. data/test/integration/http_authenticatable_test.rb +64 -0
  97. data/test/integration/lockable_test.rb +109 -0
  98. data/test/integration/oauthable_test.rb +258 -0
  99. data/test/integration/recoverable_test.rb +141 -0
  100. data/test/integration/registerable_test.rb +179 -0
  101. data/test/integration/rememberable_test.rb +179 -0
  102. data/test/integration/timeoutable_test.rb +80 -0
  103. data/test/integration/token_authenticatable_test.rb +99 -0
  104. data/test/integration/trackable_test.rb +64 -0
  105. data/test/mailers/confirmation_instructions_test.rb +84 -0
  106. data/test/mailers/reset_password_instructions_test.rb +72 -0
  107. data/test/mailers/unlock_instructions_test.rb +66 -0
  108. data/test/mapping_test.rb +95 -0
  109. data/test/models/confirmable_test.rb +221 -0
  110. data/test/models/database_authenticatable_test.rb +82 -0
  111. data/test/models/encryptable_test.rb +65 -0
  112. data/test/models/lockable_test.rb +204 -0
  113. data/test/models/oauthable_test.rb +21 -0
  114. data/test/models/recoverable_test.rb +155 -0
  115. data/test/models/rememberable_test.rb +271 -0
  116. data/test/models/timeoutable_test.rb +28 -0
  117. data/test/models/token_authenticatable_test.rb +37 -0
  118. data/test/models/trackable_test.rb +5 -0
  119. data/test/models/validatable_test.rb +99 -0
  120. data/test/models_test.rb +77 -0
  121. data/test/oauth/config_test.rb +44 -0
  122. data/test/oauth/url_helpers_test.rb +47 -0
  123. data/test/orm/active_record.rb +9 -0
  124. data/test/orm/mongoid.rb +10 -0
  125. data/test/rails_app/app/active_record/admin.rb +6 -0
  126. data/test/rails_app/app/active_record/shim.rb +2 -0
  127. data/test/rails_app/app/active_record/user.rb +8 -0
  128. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  129. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  130. data/test/rails_app/app/controllers/application_controller.rb +9 -0
  131. data/test/rails_app/app/controllers/home_controller.rb +12 -0
  132. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  133. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  134. data/test/rails_app/app/controllers/users_controller.rb +18 -0
  135. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  136. data/test/rails_app/app/mongoid/admin.rb +9 -0
  137. data/test/rails_app/app/mongoid/shim.rb +24 -0
  138. data/test/rails_app/app/mongoid/user.rb +10 -0
  139. data/test/rails_app/config/application.rb +35 -0
  140. data/test/rails_app/config/boot.rb +13 -0
  141. data/test/rails_app/config/environment.rb +5 -0
  142. data/test/rails_app/config/environments/development.rb +19 -0
  143. data/test/rails_app/config/environments/production.rb +33 -0
  144. data/test/rails_app/config/environments/test.rb +33 -0
  145. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  146. data/test/rails_app/config/initializers/devise.rb +172 -0
  147. data/test/rails_app/config/initializers/inflections.rb +2 -0
  148. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  149. data/test/rails_app/config/routes.rb +54 -0
  150. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +31 -0
  151. data/test/rails_app/db/schema.rb +52 -0
  152. data/test/rails_app/lib/shared_admin.rb +9 -0
  153. data/test/rails_app/lib/shared_user.rb +48 -0
  154. data/test/routes_test.rb +189 -0
  155. data/test/support/assertions.rb +24 -0
  156. data/test/support/helpers.rb +60 -0
  157. data/test/support/integration.rb +88 -0
  158. data/test/support/webrat/integrations/rails.rb +24 -0
  159. data/test/test_helper.rb +23 -0
  160. data/test/test_helpers_test.rb +101 -0
  161. metadata +335 -0
@@ -0,0 +1,65 @@
1
+ require 'devise/strategies/database_authenticatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Encryptable Module adds support to several encryptors.
6
+ #
7
+ # == Options
8
+ #
9
+ # Encryptable adds the following options to devise_for:
10
+ #
11
+ # * +pepper+: a random string used to provide a more secure hash.
12
+ #
13
+ # * +encryptor+: the encryptor going to be used. By default is nil.
14
+ #
15
+ # == Examples
16
+ #
17
+ # User.find(1).valid_password?('password123') # returns true/false
18
+ #
19
+ module Encryptable
20
+ extend ActiveSupport::Concern
21
+
22
+ included do
23
+ attr_reader :password, :current_password
24
+ attr_accessor :password_confirmation
25
+ end
26
+
27
+ # Generates password salt.
28
+ def password=(new_password)
29
+ self.password_salt = self.class.password_salt if new_password.present?
30
+ super
31
+ end
32
+
33
+ def authenticatable_salt
34
+ self.password_salt
35
+ end
36
+
37
+ # Verifies whether an incoming_password (ie from sign in) is the user password.
38
+ def valid_password?(incoming_password)
39
+ password_digest(incoming_password) == self.encrypted_password
40
+ end
41
+
42
+ protected
43
+
44
+ # Digests the password using the configured encryptor.
45
+ def password_digest(password)
46
+ if self.password_salt.present?
47
+ self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
48
+ end
49
+ end
50
+
51
+ module ClassMethods
52
+ Devise::Models.config(self, :pepper, :encryptor)
53
+
54
+ # Returns the class for the configured encryptor.
55
+ def encryptor_class
56
+ @encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
57
+ end
58
+
59
+ def password_salt
60
+ self.encryptor_class.salt(self.stretches)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,168 @@
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
+ #
19
+ module Lockable
20
+ extend ActiveSupport::Concern
21
+
22
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
23
+
24
+ # Lock an user setting it's locked_at to actual time.
25
+ def lock_access!
26
+ self.locked_at = Time.now
27
+
28
+ if unlock_strategy_enabled?(:email)
29
+ generate_unlock_token
30
+ send_unlock_instructions
31
+ end
32
+
33
+ save(:validate => false)
34
+ end
35
+
36
+ # Unlock an user by cleaning locket_at and failed_attempts.
37
+ def unlock_access!
38
+ if_access_locked do
39
+ self.locked_at = nil
40
+ self.failed_attempts = 0 if respond_to?(:failed_attempts=)
41
+ self.unlock_token = nil if respond_to?(:unlock_token=)
42
+ save(:validate => false)
43
+ end
44
+ end
45
+
46
+ # Verifies whether a user is locked or not.
47
+ def access_locked?
48
+ locked_at && !lock_expired?
49
+ end
50
+
51
+ # Send unlock instructions by email
52
+ def send_unlock_instructions
53
+ ::Devise.mailer.unlock_instructions(self).deliver
54
+ end
55
+
56
+ # Resend the unlock instructions if the user is locked.
57
+ def resend_unlock_token
58
+ if_access_locked { send_unlock_instructions }
59
+ end
60
+
61
+ # Overwrites active? from Devise::Models::Activatable for locking purposes
62
+ # by verifying whether an user is active to sign in or not based on locked?
63
+ def active?
64
+ super && !access_locked?
65
+ end
66
+
67
+ # Overwrites invalid_message from Devise::Models::Authenticatable to define
68
+ # the correct reason for blocking the sign in.
69
+ def inactive_message
70
+ access_locked? ? :locked : super
71
+ end
72
+
73
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
74
+ # for verifying whether an user is allowed to sign in or not. If the user
75
+ # is locked, it should never be allowed.
76
+ def valid_for_authentication?
77
+ return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
78
+
79
+ case (result = super)
80
+ when Symbol
81
+ return result
82
+ when TrueClass
83
+ self.failed_attempts = 0
84
+ when FalseClass
85
+ # PostgreSQL uses nil as the default value for integer columns set to 0
86
+ self.failed_attempts ||= 0
87
+ self.failed_attempts += 1
88
+ if attempts_exceeded?
89
+ lock_access!
90
+ return :locked
91
+ end
92
+ end
93
+
94
+ save(:validate => false) if changed?
95
+ result
96
+ end
97
+
98
+ protected
99
+
100
+ def attempts_exceeded?
101
+ self.failed_attempts > self.class.maximum_attempts
102
+ end
103
+
104
+ # Generates unlock token
105
+ def generate_unlock_token
106
+ self.unlock_token = self.class.unlock_token
107
+ end
108
+
109
+ # Tells if the lock is expired if :time unlock strategy is active
110
+ def lock_expired?
111
+ if unlock_strategy_enabled?(:time)
112
+ locked_at && locked_at < self.class.unlock_in.ago
113
+ else
114
+ false
115
+ end
116
+ end
117
+
118
+ # Checks whether the record is locked or not, yielding to the block
119
+ # if it's locked, otherwise adds an error to email.
120
+ def if_access_locked
121
+ if access_locked?
122
+ yield
123
+ else
124
+ self.errors.add(:email, :not_locked)
125
+ false
126
+ end
127
+ end
128
+
129
+ module ClassMethods
130
+ # Attempt to find a user by it's email. If a record is found, send new
131
+ # unlock instructions to it. If not user is found, returns a new user
132
+ # with an email not found error.
133
+ # Options must contain the user email
134
+ def send_unlock_instructions(attributes={})
135
+ lockable = find_or_initialize_with_errors(authentication_keys, attributes, :not_found)
136
+ lockable.resend_unlock_token if lockable.persisted?
137
+ lockable
138
+ end
139
+
140
+ # Find a user by it's unlock token and try to unlock it.
141
+ # If no user is found, returns a new user with an error.
142
+ # If the user is not locked, creates an error for the user
143
+ # Options must have the unlock_token
144
+ def unlock_access_by_token(unlock_token)
145
+ lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
146
+ lockable.unlock_access! if lockable.persisted?
147
+ lockable
148
+ end
149
+
150
+ # Is the unlock enabled for the given unlock strategy?
151
+ def unlock_strategy_enabled?(strategy)
152
+ [:both, strategy].include?(self.unlock_strategy)
153
+ end
154
+
155
+ # Is the lock enabled for the given lock strategy?
156
+ def lock_strategy_enabled?(strategy)
157
+ self.lock_strategy == strategy
158
+ end
159
+
160
+ def unlock_token
161
+ Devise.friendly_token
162
+ end
163
+
164
+ Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in)
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,49 @@
1
+ module Devise
2
+ module Models
3
+ # Adds OAuth support to your model. The whole workflow is deeply discussed in the
4
+ # README. This module adds just a class +oauth_access_token+ helper to your model
5
+ # which assists you on creating an access token. All the other OAuth hooks in
6
+ # Devise must be implemented by yourself in your application.
7
+ #
8
+ # == Options
9
+ #
10
+ # Oauthable adds the following options to devise_for:
11
+ #
12
+ # * +oauth_providers+: Which providers are avaialble to this model. It expects an array:
13
+ #
14
+ # devise_for :database_authenticatable, :oauthable, :oauth_providers => [:twitter]
15
+ #
16
+ module Oauthable
17
+ extend ActiveSupport::Concern
18
+
19
+ module ClassMethods
20
+ def oauth_configs #:nodoc:
21
+ Devise.oauth_configs.slice(*oauth_providers)
22
+ end
23
+
24
+ # Pass a token stored in the database to this object to get an OAuth2::AccessToken
25
+ # object back, as the one received in your model hook.
26
+ #
27
+ # For each provider you add, you may want to add a hook to retrieve the token based
28
+ # on the column you stored the token in the database. For example, you may want to
29
+ # the following for twitter:
30
+ #
31
+ # def oauth_twitter_token
32
+ # @oauth_twitter_token ||= self.class.oauth_access_token(:twitter, twitter_token)
33
+ # end
34
+ #
35
+ # You can call get, post, put and delete in this object to access Twitter's API.
36
+ def oauth_access_token(provider, token)
37
+ oauth_configs[provider].access_token_by_token(token)
38
+ end
39
+
40
+ # TODO Implement this method in the future.
41
+ # def refresh_oauth_token(provider, refresh_token)
42
+ # returns access_token
43
+ # end
44
+
45
+ Devise::Models.config(self, :oauth_providers)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,83 @@
1
+ module Devise
2
+ module Models
3
+
4
+ # Recoverable takes care of reseting the user password and send reset instructions.
5
+ #
6
+ # == Examples
7
+ #
8
+ # # resets the user password and save the record, true if valid passwords are given, otherwise false
9
+ # User.find(1).reset_password!('password123', 'password123')
10
+ #
11
+ # # only resets the user password, without saving the record
12
+ # user = User.find(1)
13
+ # user.reset_password('password123', 'password123')
14
+ #
15
+ # # creates a new token and send it with instructions about how to reset the password
16
+ # User.find(1).send_reset_password_instructions
17
+ #
18
+ module Recoverable
19
+ extend ActiveSupport::Concern
20
+
21
+ # Update password saving the record and clearing token. Returns true if
22
+ # the passwords are valid and the record was saved, false otherwise.
23
+ def reset_password!(new_password, new_password_confirmation)
24
+ self.password = new_password
25
+ self.password_confirmation = new_password_confirmation
26
+ clear_reset_password_token if valid?
27
+ save
28
+ end
29
+
30
+ # Resets reset password token and send reset password instructions by email
31
+ def send_reset_password_instructions
32
+ generate_reset_password_token!
33
+ ::Devise.mailer.reset_password_instructions(self).deliver
34
+ end
35
+
36
+ protected
37
+
38
+ # Generates a new random token for reset password
39
+ def generate_reset_password_token
40
+ self.reset_password_token = self.class.reset_password_token
41
+ end
42
+
43
+ # Resets the reset password token with and save the record without
44
+ # validating
45
+ def generate_reset_password_token!
46
+ generate_reset_password_token && save(:validate => false)
47
+ end
48
+
49
+ # Removes reset_password token
50
+ def clear_reset_password_token
51
+ self.reset_password_token = nil
52
+ end
53
+
54
+ module ClassMethods
55
+ # Attempt to find a user by it's email. If a record is found, send new
56
+ # password instructions to it. If not user is found, returns a new user
57
+ # with an email not found error.
58
+ # Attributes must contain the user email
59
+ def send_reset_password_instructions(attributes={})
60
+ recoverable = find_or_initialize_with_errors(authentication_keys, attributes, :not_found)
61
+ recoverable.send_reset_password_instructions if recoverable.persisted?
62
+ recoverable
63
+ end
64
+
65
+ # Generate a token checking if one does not already exist in the database.
66
+ def reset_password_token
67
+ generate_token(:reset_password_token)
68
+ end
69
+
70
+ # Attempt to find a user by it's reset_password_token to reset it's
71
+ # password. If a user is found, reset it's password and automatically
72
+ # try saving the record. If not user is found, returns a new user
73
+ # containing an error in reset_password_token attribute.
74
+ # Attributes must contain reset_password_token, password and confirmation
75
+ def reset_password_by_token(attributes={})
76
+ recoverable = find_or_initialize_with_error_by(:reset_password_token, attributes[:reset_password_token])
77
+ recoverable.reset_password!(attributes[:password], attributes[:password_confirmation]) if recoverable.persisted?
78
+ recoverable
79
+ end
80
+ end
81
+ end
82
+ end
83
+ 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,122 @@
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
+ respond_to?(:remember_token) ? self.remember_token : self.authenticatable_salt
80
+ end
81
+
82
+ def cookie_options
83
+ self.class.cookie_options
84
+ end
85
+
86
+ protected
87
+
88
+ # Generate a token unless remember_across_browsers is true and there is
89
+ # an existing remember_token or the existing remember_token has expried.
90
+ def generate_remember_token? #:nodoc:
91
+ !(self.class.remember_across_browsers && remember_token) || remember_expired?
92
+ end
93
+
94
+ # Generate a timestamp if extend_remember_period is true, if no remember_token
95
+ # exists, or if an existing remember token has expired.
96
+ def generate_remember_timestamp?(extend_period) #:nodoc:
97
+ extend_period || remember_created_at.nil? || remember_expired?
98
+ end
99
+
100
+ module ClassMethods
101
+ # Create the cookie key using the record id and remember_token
102
+ def serialize_into_cookie(record)
103
+ [record.id, record.rememberable_value]
104
+ end
105
+
106
+ # Recreate the user based on the stored cookie
107
+ def serialize_from_cookie(id, remember_token)
108
+ record = find(:first, :conditions => { :id => id })
109
+ record if record && record.rememberable_value == remember_token && !record.remember_expired?
110
+ end
111
+
112
+ # Generate a token checking if one does not already exist in the database.
113
+ def remember_token
114
+ generate_token(:remember_token)
115
+ end
116
+
117
+ Devise::Models.config(self, :remember_for, :remember_across_browsers,
118
+ :extend_remember_period, :cookie_options)
119
+ end
120
+ end
121
+ end
122
+ end