devise 1.1.9 → 1.2.rc
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of devise might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +34 -26
- data/README.rdoc +134 -100
- data/app/controllers/devise/confirmations_controller.rb +1 -1
- data/app/controllers/devise/omniauth_callbacks_controller.rb +26 -0
- data/app/controllers/devise/passwords_controller.rb +1 -1
- data/app/controllers/devise/registrations_controller.rb +59 -6
- data/app/controllers/devise/sessions_controller.rb +3 -2
- data/app/controllers/devise/unlocks_controller.rb +1 -1
- data/app/helpers/devise_helper.rb +4 -2
- data/app/mailers/devise/mailer.rb +27 -10
- data/app/views/devise/confirmations/new.html.erb +1 -1
- data/app/views/devise/passwords/edit.html.erb +2 -2
- data/app/views/devise/passwords/new.html.erb +1 -1
- data/app/views/devise/registrations/edit.html.erb +1 -1
- data/app/views/devise/registrations/new.html.erb +1 -1
- data/app/views/devise/sessions/new.html.erb +1 -1
- data/app/views/devise/shared/_links.erb +6 -0
- data/app/views/devise/unlocks/new.html.erb +1 -1
- data/config/locales/en.yml +9 -2
- data/lib/devise.rb +116 -58
- data/lib/devise/controllers/helpers.rb +103 -107
- data/lib/devise/controllers/internal_helpers.rb +23 -7
- data/lib/devise/controllers/scoped_views.rb +4 -6
- data/lib/devise/controllers/url_helpers.rb +3 -5
- data/lib/devise/encryptors/base.rb +1 -1
- data/lib/devise/encryptors/restful_authentication_sha1.rb +4 -4
- data/lib/devise/failure_app.rb +29 -21
- data/lib/devise/hooks/forgetable.rb +2 -1
- data/lib/devise/hooks/rememberable.rb +11 -9
- data/lib/devise/mapping.rb +12 -5
- data/lib/devise/models.rb +0 -14
- data/lib/devise/models/authenticatable.rb +40 -30
- data/lib/devise/models/confirmable.rb +11 -15
- data/lib/devise/models/database_authenticatable.rb +23 -35
- data/lib/devise/models/encryptable.rb +65 -0
- data/lib/devise/models/lockable.rb +8 -7
- data/lib/devise/models/omniauthable.rb +23 -0
- data/lib/devise/models/recoverable.rb +5 -3
- data/lib/devise/models/registerable.rb +13 -0
- data/lib/devise/models/rememberable.rb +38 -30
- data/lib/devise/models/timeoutable.rb +20 -3
- data/lib/devise/models/token_authenticatable.rb +19 -7
- data/lib/devise/models/validatable.rb +16 -4
- data/lib/devise/modules.rb +15 -8
- data/lib/devise/omniauth.rb +47 -0
- data/lib/devise/omniauth/config.rb +30 -0
- data/lib/devise/omniauth/test_helpers.rb +57 -0
- data/lib/devise/omniauth/url_helpers.rb +29 -0
- data/lib/devise/orm/active_record.rb +2 -0
- data/lib/devise/orm/mongoid.rb +4 -2
- data/lib/devise/rails.rb +26 -46
- data/lib/devise/rails/routes.rb +64 -20
- data/lib/devise/rails/warden_compat.rb +18 -20
- data/lib/devise/schema.rb +13 -14
- data/lib/devise/strategies/authenticatable.rb +33 -7
- data/lib/devise/strategies/database_authenticatable.rb +1 -1
- data/lib/devise/strategies/rememberable.rb +1 -1
- data/lib/devise/strategies/token_authenticatable.rb +6 -2
- data/lib/devise/test_helpers.rb +11 -1
- data/lib/devise/version.rb +1 -1
- data/lib/generators/active_record/templates/migration.rb +1 -0
- data/lib/generators/devise/orm_helpers.rb +3 -2
- data/lib/generators/templates/devise.rb +70 -39
- data/test/controllers/helpers_test.rb +43 -67
- data/test/controllers/internal_helpers_test.rb +29 -8
- data/test/controllers/url_helpers_test.rb +2 -1
- data/test/failure_app_test.rb +56 -21
- data/test/generators/generators_test_helper.rb +4 -0
- data/test/generators/install_generator_test.rb +14 -0
- data/test/generators/views_generator_test.rb +37 -0
- data/test/integration/authenticatable_test.rb +147 -62
- data/test/integration/database_authenticatable_test.rb +22 -0
- data/test/integration/http_authenticatable_test.rb +12 -2
- data/test/integration/omniauthable_test.rb +107 -0
- data/test/integration/recoverable_test.rb +39 -20
- data/test/integration/registerable_test.rb +30 -4
- data/test/integration/rememberable_test.rb +57 -34
- data/test/integration/timeoutable_test.rb +10 -1
- data/test/integration/token_authenticatable_test.rb +12 -17
- data/test/mailers/confirmation_instructions_test.rb +4 -0
- data/test/mailers/reset_password_instructions_test.rb +4 -0
- data/test/mailers/unlock_instructions_test.rb +4 -0
- data/test/mapping_test.rb +37 -3
- data/test/models/confirmable_test.rb +3 -3
- data/test/models/database_authenticatable_test.rb +14 -71
- data/test/models/encryptable_test.rb +65 -0
- data/test/models/lockable_test.rb +17 -1
- data/test/models/recoverable_test.rb +17 -0
- data/test/models/rememberable_test.rb +186 -125
- data/test/models/token_authenticatable_test.rb +1 -13
- data/test/models_test.rb +5 -5
- data/test/omniauth/url_helpers_test.rb +47 -0
- data/test/rails_app/app/active_record/admin.rb +4 -1
- data/test/rails_app/app/active_record/user.rb +5 -4
- data/test/rails_app/app/controllers/{sessions_controller.rb → admins/sessions_controller.rb} +1 -1
- data/test/rails_app/app/controllers/home_controller.rb +9 -0
- data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +7 -0
- data/test/rails_app/app/mongoid/admin.rb +4 -1
- data/test/rails_app/app/mongoid/shim.rb +16 -3
- data/test/rails_app/app/mongoid/user.rb +5 -5
- data/test/rails_app/config/initializers/devise.rb +52 -28
- data/test/rails_app/config/routes.rb +14 -6
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +21 -17
- data/test/rails_app/db/schema.rb +17 -51
- data/test/rails_app/lib/shared_admin.rb +9 -0
- data/test/rails_app/lib/shared_user.rb +23 -0
- data/test/routes_test.rb +42 -9
- data/test/support/integration.rb +3 -3
- data/test/support/webrat/integrations/rails.rb +7 -0
- data/test/test_helper.rb +2 -0
- data/test/test_helpers_test.rb +29 -0
- metadata +60 -30
- data/Gemfile +0 -27
- data/Gemfile.lock +0 -115
- data/Rakefile +0 -55
- data/TODO +0 -3
- data/lib/devise/encryptors/bcrypt.rb +0 -19
- data/lib/generators/devise_install_generator.rb +0 -4
- data/lib/generators/devise_views_generator.rb +0 -4
- data/test/indifferent_hash.rb +0 -33
- data/test/support/test_silencer.rb +0 -5
@@ -5,7 +5,8 @@
|
|
5
5
|
Warden::Manager.before_logout do |record, warden, options|
|
6
6
|
if record.respond_to?(:forget_me!)
|
7
7
|
record.forget_me! unless record.frozen?
|
8
|
-
cookie_options =
|
8
|
+
cookie_options = Rails.configuration.session_options.slice(:path, :domain, :secure)
|
9
|
+
cookie_options.merge!(record.cookie_options)
|
9
10
|
warden.cookies.delete("remember_#{options[:scope]}_token", cookie_options)
|
10
11
|
end
|
11
12
|
end
|
@@ -10,20 +10,22 @@ module Devise
|
|
10
10
|
|
11
11
|
if succeeded? && resource.respond_to?(:remember_me!) && remember_me?
|
12
12
|
resource.remember_me!(extend_remember_period?)
|
13
|
-
|
14
|
-
configuration = {
|
15
|
-
:value => resource.class.serialize_into_cookie(resource),
|
16
|
-
:expires => resource.remember_expires_at,
|
17
|
-
:path => "/"
|
18
|
-
}
|
19
|
-
|
20
|
-
configuration[:domain] = resource.cookie_domain if resource.cookie_domain?
|
21
|
-
cookies.signed["remember_#{scope}_token"] = configuration
|
13
|
+
cookies.signed["remember_#{scope}_token"] = cookie_values(resource)
|
22
14
|
end
|
23
15
|
end
|
24
16
|
|
25
17
|
protected
|
26
18
|
|
19
|
+
def cookie_values(resource)
|
20
|
+
options = Rails.configuration.session_options.slice(:path, :domain, :secure)
|
21
|
+
options.merge!(resource.cookie_options)
|
22
|
+
options.merge!(
|
23
|
+
:value => resource.class.serialize_into_cookie(resource),
|
24
|
+
:expires => resource.remember_expires_at
|
25
|
+
)
|
26
|
+
options
|
27
|
+
end
|
28
|
+
|
27
29
|
def succeeded?
|
28
30
|
@result == :success
|
29
31
|
end
|
data/lib/devise/mapping.rb
CHANGED
@@ -22,7 +22,7 @@ module Devise
|
|
22
22
|
# # is the modules included in the class
|
23
23
|
#
|
24
24
|
class Mapping #:nodoc:
|
25
|
-
attr_reader :singular, :
|
25
|
+
attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via
|
26
26
|
alias :name :singular
|
27
27
|
|
28
28
|
# Receives an object and find a scope for it. If a scope cannot be found,
|
@@ -37,12 +37,17 @@ module Devise
|
|
37
37
|
Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
|
38
38
|
end
|
39
39
|
|
40
|
-
raise "Could not find a valid mapping for #{duck}"
|
40
|
+
raise "Could not find a valid mapping for #{duck.inspect}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.find_by_path!(path, path_type=:fullpath)
|
44
|
+
Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
|
45
|
+
raise "Could not find a valid mapping for path #{path.inspect}"
|
41
46
|
end
|
42
47
|
|
43
48
|
def initialize(name, options) #:nodoc:
|
44
|
-
@
|
45
|
-
@singular = (options[:singular] || @
|
49
|
+
@scoped_path = options[:as] ? "#{options[:as]}/#{name}" : name.to_s
|
50
|
+
@singular = (options[:singular] || @scoped_path.tr('/', '_').singularize).to_sym
|
46
51
|
|
47
52
|
@class_name = (options[:class_name] || name.to_s.classify).to_s
|
48
53
|
@ref = ActiveSupport::Dependencies.ref(@class_name)
|
@@ -57,6 +62,8 @@ module Devise
|
|
57
62
|
@path_names = Hash.new { |h,k| h[k] = k.to_s }
|
58
63
|
@path_names.merge!(:registration => "")
|
59
64
|
@path_names.merge!(options[:path_names] || {})
|
65
|
+
|
66
|
+
@sign_out_via = options[:sign_out_via] || Devise.sign_out_via
|
60
67
|
end
|
61
68
|
|
62
69
|
# Return modules for the mapping.
|
@@ -82,7 +89,7 @@ module Devise
|
|
82
89
|
end
|
83
90
|
|
84
91
|
def fullpath
|
85
|
-
"
|
92
|
+
"/#{@path_prefix}/#{@path}".squeeze("/")
|
86
93
|
end
|
87
94
|
|
88
95
|
# Create magic predicates for verifying what module is activated by this map.
|
data/lib/devise/models.rb
CHANGED
@@ -47,20 +47,6 @@ module Devise
|
|
47
47
|
def devise(*modules)
|
48
48
|
include Devise::Models::Authenticatable
|
49
49
|
options = modules.extract_options!
|
50
|
-
|
51
|
-
if modules.delete(:authenticatable)
|
52
|
-
ActiveSupport::Deprecation.warn ":authenticatable as module is deprecated. Please give :database_authenticatable instead.", caller
|
53
|
-
modules << :database_authenticatable
|
54
|
-
end
|
55
|
-
|
56
|
-
if modules.delete(:activatable)
|
57
|
-
ActiveSupport::Deprecation.warn ":activatable as module is deprecated. It's included in your model by default.", caller
|
58
|
-
end
|
59
|
-
|
60
|
-
if modules.delete(:http_authenticatable)
|
61
|
-
ActiveSupport::Deprecation.warn ":http_authenticatable as module is deprecated and is on by default. Revert by setting :http_authenticatable => false.", caller
|
62
|
-
end
|
63
|
-
|
64
50
|
self.devise_modules += Devise::ALL & modules.map(&:to_sym).uniq
|
65
51
|
|
66
52
|
devise_modules_hook! do
|
@@ -4,18 +4,25 @@ module Devise
|
|
4
4
|
module Models
|
5
5
|
# Authenticable module. Holds common settings for authentication.
|
6
6
|
#
|
7
|
-
# ==
|
7
|
+
# == Options
|
8
8
|
#
|
9
|
-
#
|
10
|
-
# using devise method or overwriting the respective instance method.
|
9
|
+
# Authenticatable adds the following options to devise_for:
|
11
10
|
#
|
12
|
-
# authentication_keys
|
11
|
+
# * +authentication_keys+: parameters used for authentication. By default [:email].
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#
|
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
16
|
#
|
17
|
-
#
|
18
|
-
#
|
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.
|
19
26
|
#
|
20
27
|
# == Active?
|
21
28
|
#
|
@@ -66,8 +73,11 @@ module Devise
|
|
66
73
|
:inactive
|
67
74
|
end
|
68
75
|
|
76
|
+
def authenticatable_salt
|
77
|
+
end
|
78
|
+
|
69
79
|
module ClassMethods
|
70
|
-
Devise::Models.config(self, :authentication_keys, :http_authenticatable, :params_authenticatable)
|
80
|
+
Devise::Models.config(self, :authentication_keys, :request_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
|
71
81
|
|
72
82
|
def params_authenticatable?(strategy)
|
73
83
|
params_authenticatable.is_a?(Array) ?
|
@@ -90,44 +100,44 @@ module Devise
|
|
90
100
|
# end
|
91
101
|
#
|
92
102
|
def find_for_authentication(conditions)
|
93
|
-
|
94
|
-
|
103
|
+
case_insensitive_keys.each { |k| conditions[k].try(:downcase!) }
|
104
|
+
to_adapter.find_first(conditions)
|
95
105
|
end
|
96
106
|
|
97
107
|
# Find an initialize a record setting an error if it can't be found.
|
98
108
|
def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
109
|
+
find_or_initialize_with_errors([attribute], { attribute => value }, error)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find an initialize a group of attributes based on a list of required attributes.
|
113
|
+
def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
|
114
|
+
case_insensitive_keys.each { |k| attributes[k].try(:downcase!) }
|
115
|
+
|
116
|
+
attributes = attributes.slice(*required_attributes)
|
117
|
+
attributes.delete_if { |key, value| value.blank? }
|
103
118
|
|
119
|
+
if attributes.size == required_attributes.size
|
120
|
+
record = to_adapter.find_first(attributes)
|
121
|
+
end
|
122
|
+
|
104
123
|
unless record
|
105
124
|
record = new
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
125
|
+
|
126
|
+
required_attributes.each do |key|
|
127
|
+
value = attributes[key]
|
128
|
+
record.send("#{key}=", value)
|
129
|
+
record.errors.add(key, value.present? ? error : :blank)
|
110
130
|
end
|
111
|
-
record.errors.add(attribute, error)
|
112
131
|
end
|
113
132
|
|
114
133
|
record
|
115
134
|
end
|
116
135
|
|
117
|
-
protected
|
118
|
-
|
119
|
-
# Force keys to be string to avoid injection on mongoid related database.
|
120
|
-
def filter_auth_params(conditions)
|
121
|
-
conditions.each do |k, v|
|
122
|
-
conditions[k] = v.to_s
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
136
|
# Generate a token by looping and ensuring does not already exist.
|
127
137
|
def generate_token(column)
|
128
138
|
loop do
|
129
139
|
token = Devise.friendly_token
|
130
|
-
break token unless
|
140
|
+
break token unless to_adapter.find_first({ column => token })
|
131
141
|
end
|
132
142
|
end
|
133
143
|
end
|
@@ -3,28 +3,24 @@ module Devise
|
|
3
3
|
# Confirmable is responsible to verify if an account is already confirmed to
|
4
4
|
# sign in, and to send emails with confirmation instructions.
|
5
5
|
# Confirmation instructions are sent to the user email after creating a
|
6
|
-
# record
|
7
|
-
# a new confirmation instruction request.
|
8
|
-
# Whenever the user update it's email, his account is automatically unconfirmed,
|
9
|
-
# it means it won't be able to sign in again without confirming the account
|
10
|
-
# again through the email that was sent.
|
6
|
+
# record and when manually requested by a new confirmation instruction request.
|
11
7
|
#
|
12
|
-
#
|
8
|
+
# == Options
|
13
9
|
#
|
14
|
-
#
|
15
|
-
# without blocking his access. When confirm_within is zero, the
|
16
|
-
# user won't be able to sign in without confirming. You can
|
17
|
-
# use this to let your user access some features of your
|
18
|
-
# application without confirming the account, but blocking it
|
19
|
-
# after a certain period (ie 7 days). By default confirm_within is
|
20
|
-
# zero, it means users always have to confirm to sign in.
|
10
|
+
# Confirmable adds the following options to devise_for:
|
21
11
|
#
|
22
|
-
#
|
12
|
+
# * +confirm_within+: 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 confirm_within is zero, it means users always have to confirm to sign in.
|
17
|
+
#
|
18
|
+
# == Examples
|
23
19
|
#
|
24
20
|
# User.find(1).confirm! # returns true unless it's already confirmed
|
25
21
|
# User.find(1).confirmed? # true/false
|
26
22
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
27
|
-
#
|
23
|
+
#
|
28
24
|
module Confirmable
|
29
25
|
extend ActiveSupport::Concern
|
30
26
|
|
@@ -1,25 +1,18 @@
|
|
1
1
|
require 'devise/strategies/database_authenticatable'
|
2
|
+
require 'bcrypt'
|
2
3
|
|
3
4
|
module Devise
|
4
5
|
module Models
|
5
6
|
# Authenticable Module, responsible for encrypting password and validating
|
6
7
|
# authenticity of a user while signing in.
|
7
8
|
#
|
8
|
-
#
|
9
|
+
# == Options
|
9
10
|
#
|
10
|
-
#
|
11
|
-
# using devise method or overwriting the respective instance method.
|
11
|
+
# DatabaseAuthenticable adds the following options to devise_for:
|
12
12
|
#
|
13
|
-
#
|
14
|
-
# password changes, it's gonna be encrypted again, and this key
|
15
|
-
# is added to the password and salt to create a secure hash.
|
16
|
-
# Always use `rake secret' to generate a new key.
|
13
|
+
# * +stretches+: the cost given to bcrypt.
|
17
14
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# encryptor: the encryptor going to be used. By default :sha1.
|
21
|
-
#
|
22
|
-
# Examples:
|
15
|
+
# == Examples
|
23
16
|
#
|
24
17
|
# User.find(1).valid_password?('password123') # returns true/false
|
25
18
|
#
|
@@ -29,22 +22,18 @@ module Devise
|
|
29
22
|
included do
|
30
23
|
attr_reader :password, :current_password
|
31
24
|
attr_accessor :password_confirmation
|
25
|
+
before_save :downcase_keys
|
32
26
|
end
|
33
27
|
|
34
|
-
#
|
35
|
-
# and then trigger any "after_changed_password"-callbacks.
|
28
|
+
# Generates password encryption based on the given value.
|
36
29
|
def password=(new_password)
|
37
30
|
@password = new_password
|
38
|
-
|
39
|
-
if @password.present?
|
40
|
-
self.password_salt = self.class.password_salt
|
41
|
-
self.encrypted_password = password_digest(@password)
|
42
|
-
end
|
31
|
+
self.encrypted_password = password_digest(@password) if @password.present?
|
43
32
|
end
|
44
33
|
|
45
34
|
# Verifies whether an incoming_password (ie from sign in) is the user password.
|
46
|
-
def valid_password?(
|
47
|
-
|
35
|
+
def valid_password?(password)
|
36
|
+
::BCrypt::Password.new(self.encrypted_password) == "#{password}#{self.class.pepper}"
|
48
37
|
end
|
49
38
|
|
50
39
|
# Set password and password confirmation to nil
|
@@ -78,26 +67,25 @@ module Devise
|
|
78
67
|
def after_database_authentication
|
79
68
|
end
|
80
69
|
|
70
|
+
# A reliable way to expose the salt regardless of the implementation.
|
71
|
+
def authenticatable_salt
|
72
|
+
self.encrypted_password[0,29] if self.encrypted_password
|
73
|
+
end
|
74
|
+
|
81
75
|
protected
|
82
76
|
|
83
|
-
#
|
77
|
+
# Downcase case-insensitive keys
|
78
|
+
def downcase_keys
|
79
|
+
self.class.case_insensitive_keys.each { |k| self[k].try(:downcase!) }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Digests the password using bcrypt.
|
84
83
|
def password_digest(password)
|
85
|
-
|
86
|
-
self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
|
87
|
-
end
|
84
|
+
::BCrypt::Password.create("#{password}#{self.class.pepper}", :cost => self.class.stretches).to_s
|
88
85
|
end
|
89
86
|
|
90
87
|
module ClassMethods
|
91
|
-
Devise::Models.config(self, :pepper, :stretches
|
92
|
-
|
93
|
-
# Returns the class for the configured encryptor.
|
94
|
-
def encryptor_class
|
95
|
-
@encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
|
96
|
-
end
|
97
|
-
|
98
|
-
def password_salt
|
99
|
-
self.encryptor_class.salt(self.stretches)
|
100
|
-
end
|
88
|
+
Devise::Models.config(self, :pepper, :stretches)
|
101
89
|
|
102
90
|
# We assume this method already gets the sanitized values from the
|
103
91
|
# DatabaseAuthenticatable strategy. If you are using this method on
|
@@ -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, :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
|
@@ -7,13 +7,14 @@ module Devise
|
|
7
7
|
# will unlock the user automatically after some configured time (ie 2.hours).
|
8
8
|
# It's also possible to setup lockable to use both email and time strategies.
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# == Options
|
11
11
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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.
|
17
18
|
#
|
18
19
|
module Lockable
|
19
20
|
extend ActiveSupport::Concern
|
@@ -131,7 +132,7 @@ module Devise
|
|
131
132
|
# with an email not found error.
|
132
133
|
# Options must contain the user email
|
133
134
|
def send_unlock_instructions(attributes={})
|
134
|
-
lockable =
|
135
|
+
lockable = find_or_initialize_with_errors(authentication_keys, attributes, :not_found)
|
135
136
|
lockable.resend_unlock_token if lockable.persisted?
|
136
137
|
lockable
|
137
138
|
end
|