PageRenderTime 127ms CodeModel.GetById 45ms RepoModel.GetById 0ms app.codeStats 1ms

/ritter/vendor/plugins/authlogic/lib/authlogic/acts_as_authentic/password.rb

https://github.com/kmkkmk/app
Ruby | 263 lines | 161 code | 36 blank | 66 comment | 22 complexity | b2006b8a9286734116fa79a5ab996c70 MD5 | raw file
  1. module Authlogic
  2. module ActsAsAuthentic
  3. # This module has a lot of neat functionality. It is responsible for encrypting your password, salting it, and verifying it.
  4. # It can also help you transition to a new encryption algorithm. See the Config sub module for configuration options.
  5. module Password
  6. def self.included(klass)
  7. klass.class_eval do
  8. extend Config
  9. add_acts_as_authentic_module(Callbacks)
  10. add_acts_as_authentic_module(Methods)
  11. end
  12. end
  13. # All configuration for the password aspect of acts_as_authentic.
  14. module Config
  15. # The name of the crypted_password field in the database.
  16. #
  17. # * <tt>Default:</tt> :crypted_password, :encrypted_password, :password_hash, or :pw_hash
  18. # * <tt>Accepts:</tt> Symbol
  19. def crypted_password_field(value = nil)
  20. config(:crypted_password_field, value, first_column_to_exist(nil, :crypted_password, :encrypted_password, :password_hash, :pw_hash))
  21. end
  22. alias_method :crypted_password_field=, :crypted_password_field
  23. # The name of the password_salt field in the database.
  24. #
  25. # * <tt>Default:</tt> :password_salt, :pw_salt, :salt, nil if none exist
  26. # * <tt>Accepts:</tt> Symbol
  27. def password_salt_field(value = nil)
  28. config(:password_salt_field, value, first_column_to_exist(nil, :password_salt, :pw_salt, :salt))
  29. end
  30. alias_method :password_salt_field=, :password_salt_field
  31. # By default passwords are required when a record is new or the crypted_password is blank, but if both of these things
  32. # are met a password is not required. In this case, blank passwords are ignored.
  33. #
  34. # Think about a profile page, where the user can edit all of their information, including changing their password.
  35. # If they do not want to change their password they just leave the fields blank. This will try to set the password to
  36. # a blank value, in which case is incorrect behavior. As such, Authlogic ignores this. But let's say you have a completely
  37. # separate page for resetting passwords, you might not want to ignore blank passwords. If this is the case for you, then
  38. # just set this value to false.
  39. #
  40. # * <tt>Default:</tt> true
  41. # * <tt>Accepts:</tt> Boolean
  42. def ignore_blank_passwords(value = nil)
  43. config(:ignore_blank_passwords, value, true)
  44. end
  45. alias_method :ignore_blank_passwords=, :ignore_blank_passwords
  46. # Whether or not to validate the password field.
  47. #
  48. # * <tt>Default:</tt> true
  49. # * <tt>Accepts:</tt> Boolean
  50. def validate_password_field(value = nil)
  51. config(:validate_password_field, value, true)
  52. end
  53. alias_method :validate_password_field=, :validate_password_field
  54. # A hash of options for the validates_length_of call for the password field. Allows you to change this however you want.
  55. #
  56. # * <tt>Default:</tt> {:minimum => 4, :if => :require_password?}
  57. # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
  58. def validates_length_of_password_field_options(value = nil)
  59. config(:validates_length_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
  60. end
  61. alias_method :validates_length_of_password_field_options=, :validates_length_of_password_field_options
  62. # A hash of options for the validates_confirmation_of call for the password field. Allows you to change this however you want.
  63. #
  64. # * <tt>Default:</tt> {:minimum => 4, :if => "#{password_salt_field}_changed?".to_sym}
  65. # * <tt>Accepts:</tt> Hash of options accepted by validates_confirmation_of
  66. def validates_confirmation_of_password_field_options(value = nil)
  67. config(:validates_confirmation_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
  68. end
  69. alias_method :validates_confirmation_of_password_field_options=, :validates_confirmation_of_password_field_options
  70. # A hash of options for the validates_length_of call for the password_confirmation field. Allows you to change this however you want.
  71. #
  72. # * <tt>Default:</tt> validates_length_of_password_field_options
  73. # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
  74. def validates_length_of_password_confirmation_field_options(value = nil)
  75. config(:validates_length_of_password_confirmation_field_options, value, validates_length_of_password_field_options)
  76. end
  77. alias_method :validates_length_of_password_confirmation_field_options=, :validates_length_of_password_confirmation_field_options
  78. # The class you want to use to encrypt and verify your encrypted passwords. See the Authlogic::CryptoProviders module for more info
  79. # on the available methods and how to create your own.
  80. #
  81. # * <tt>Default:</tt> CryptoProviders::Sha512
  82. # * <tt>Accepts:</tt> Class
  83. def crypto_provider(value = nil)
  84. config(:crypto_provider, value, CryptoProviders::Sha512)
  85. end
  86. alias_method :crypto_provider=, :crypto_provider
  87. # Let's say you originally encrypted your passwords with Sha1. Sha1 is starting to join the party with MD5 and you want to switch
  88. # to something stronger. No problem, just specify your new and improved algorithm with the crypt_provider option and then let
  89. # Authlogic know you are transitioning from Sha1 using this option. Authlogic will take care of everything, including transitioning
  90. # your users to the new algorithm. The next time a user logs in, they will be granted access using the old algorithm and their
  91. # password will be resaved with the new algorithm. All new users will obviously use the new algorithm as well.
  92. #
  93. # Lastly, if you want to transition again, you can pass an array of crypto providers. So you can transition from as many algorithms
  94. # as you want.
  95. #
  96. # * <tt>Default:</tt> nil
  97. # * <tt>Accepts:</tt> Class or Array
  98. def transition_from_crypto_providers(value = nil)
  99. config(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
  100. end
  101. alias_method :transition_from_crypto_providers=, :transition_from_crypto_providers
  102. end
  103. # Callbacks / hooks to allow other modules to modify the behavior of this module.
  104. module Callbacks
  105. METHODS = [
  106. "before_password_set", "after_password_set",
  107. "before_password_verification", "after_password_verification"
  108. ]
  109. def self.included(klass)
  110. return if klass.crypted_password_field.nil?
  111. klass.define_callbacks *METHODS
  112. end
  113. private
  114. METHODS.each do |method|
  115. class_eval <<-"end_eval", __FILE__, __LINE__
  116. def #{method}
  117. run_callbacks(:#{method}) { |result, object| result == false }
  118. end
  119. end_eval
  120. end
  121. end
  122. # The methods related to the password field.
  123. module Methods
  124. def self.included(klass)
  125. return if klass.crypted_password_field.nil?
  126. klass.class_eval do
  127. include InstanceMethods
  128. if validate_password_field
  129. validates_length_of :password, validates_length_of_password_field_options
  130. validates_confirmation_of :password, validates_confirmation_of_password_field_options
  131. validates_length_of :password_confirmation, validates_length_of_password_confirmation_field_options
  132. end
  133. after_save :reset_password_changed
  134. end
  135. end
  136. module InstanceMethods
  137. # The password
  138. def password
  139. @password
  140. end
  141. # This is a virtual method. Once a password is passed to it, it will create new password salt as well as encrypt
  142. # the password.
  143. def password=(pass)
  144. return if ignore_blank_passwords? && pass.blank?
  145. before_password_set
  146. @password = pass
  147. send("#{password_salt_field}=", Authlogic::Random.friendly_token) if password_salt_field
  148. send("#{crypted_password_field}=", crypto_provider.encrypt(*encrypt_arguments(@password, act_like_restful_authentication? ? :restful_authentication : nil)))
  149. @password_changed = true
  150. after_password_set
  151. end
  152. # Accepts a raw password to determine if it is the correct password or not.
  153. def valid_password?(attempted_password)
  154. return false if attempted_password.blank? || send(crypted_password_field).blank?
  155. before_password_verification
  156. crypto_providers = [crypto_provider] + transition_from_crypto_providers
  157. crypto_providers.each_with_index do |encryptor, index|
  158. # The arguments_type of for the transitioning from restful_authentication
  159. arguments_type = (act_like_restful_authentication? && index == 0) ||
  160. (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
  161. :restful_authentication : nil
  162. if encryptor.matches?(send(crypted_password_field), *encrypt_arguments(attempted_password, arguments_type))
  163. # If we are transitioning from an older encryption algorithm and the password is still using the old algorithm
  164. # then let's reset the password using the new algorithm. If the algorithm has a cost (BCrypt) and the cost has changed, update the password with
  165. # the new cost.
  166. if index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))
  167. self.password = attempted_password
  168. save(false)
  169. end
  170. after_password_verification
  171. return true
  172. end
  173. end
  174. false
  175. end
  176. # Resets the password to a random friendly token.
  177. def reset_password
  178. friendly_token = Authlogic::Random.friendly_token
  179. self.password = friendly_token
  180. self.password_confirmation = friendly_token
  181. end
  182. alias_method :randomize_password, :reset_password
  183. # Resets the password to a random friendly token and then saves the record.
  184. def reset_password!
  185. reset_password
  186. save_without_session_maintenance(false)
  187. end
  188. alias_method :randomize_password!, :reset_password!
  189. private
  190. def encrypt_arguments(raw_password, arguments_type = nil)
  191. salt = password_salt_field ? send(password_salt_field) : nil
  192. case arguments_type
  193. when :restful_authentication
  194. [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
  195. else
  196. [raw_password, salt].compact
  197. end
  198. end
  199. def require_password?
  200. new_record? || password_changed? || send(crypted_password_field).blank?
  201. end
  202. def ignore_blank_passwords?
  203. self.class.ignore_blank_passwords == true
  204. end
  205. def password_changed?
  206. @password_changed == true
  207. end
  208. def reset_password_changed
  209. @password_changed = nil
  210. end
  211. def crypted_password_field
  212. self.class.crypted_password_field
  213. end
  214. def password_salt_field
  215. self.class.password_salt_field
  216. end
  217. def crypto_provider
  218. self.class.crypto_provider
  219. end
  220. def transition_from_crypto_providers
  221. self.class.transition_from_crypto_providers
  222. end
  223. end
  224. end
  225. end
  226. end
  227. end