devise 1.0.11 → 1.1.pre
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 +6 -72
- data/Gemfile +18 -0
- data/README.rdoc +51 -46
- data/Rakefile +5 -4
- data/app/controllers/{confirmations_controller.rb → devise/confirmations_controller.rb} +2 -2
- data/app/controllers/{passwords_controller.rb → devise/passwords_controller.rb} +4 -3
- data/app/controllers/{registrations_controller.rb → devise/registrations_controller.rb} +11 -7
- data/app/controllers/{sessions_controller.rb → devise/sessions_controller.rb} +11 -8
- data/app/controllers/{unlocks_controller.rb → devise/unlocks_controller.rb} +2 -10
- data/app/models/devise/mailer.rb +55 -0
- data/app/views/{confirmations → devise/confirmations}/new.html.erb +1 -1
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/{passwords → devise/passwords}/edit.html.erb +1 -1
- data/app/views/{passwords → devise/passwords}/new.html.erb +1 -1
- data/app/views/{registrations → devise/registrations}/edit.html.erb +1 -1
- data/app/views/{registrations → devise/registrations}/new.html.erb +1 -1
- data/app/views/{sessions → devise/sessions}/new.html.erb +1 -1
- data/app/views/{shared/_devise_links.erb → devise/shared/_links.erb} +0 -0
- data/app/views/{unlocks → devise/unlocks}/new.html.erb +1 -1
- data/{lib/devise → config}/locales/en.yml +7 -1
- data/lib/devise.rb +6 -29
- data/lib/devise/controllers/helpers.rb +16 -43
- data/lib/devise/controllers/internal_helpers.rb +10 -36
- data/lib/devise/controllers/scoped_views.rb +35 -0
- data/lib/devise/failure_app.rb +7 -14
- data/lib/devise/hooks/rememberable.rb +3 -6
- data/lib/devise/hooks/trackable.rb +1 -1
- data/lib/devise/mapping.rb +17 -18
- data/lib/devise/models.rb +4 -20
- data/lib/devise/models/{database_authenticatable.rb → authenticatable.rb} +16 -28
- data/lib/devise/models/confirmable.rb +25 -23
- data/lib/devise/models/http_authenticatable.rb +3 -7
- data/lib/devise/models/lockable.rb +40 -35
- data/lib/devise/models/recoverable.rb +4 -8
- data/lib/devise/models/rememberable.rb +6 -9
- data/lib/devise/models/timeoutable.rb +1 -3
- data/lib/devise/models/token_authenticatable.rb +4 -5
- data/lib/devise/models/validatable.rb +10 -1
- data/lib/devise/orm/mongo_mapper.rb +10 -23
- data/lib/devise/rails.rb +11 -9
- data/lib/devise/rails/routes.rb +113 -107
- data/lib/devise/rails/warden_compat.rb +3 -41
- data/lib/devise/schema.rb +13 -21
- data/lib/devise/strategies/{database_authenticatable.rb → authenticatable.rb} +3 -3
- data/lib/devise/strategies/http_authenticatable.rb +4 -22
- data/lib/devise/test_helpers.rb +1 -8
- data/lib/devise/version.rb +1 -1
- data/lib/generators/devise/devise_generator.rb +57 -0
- data/{generators → lib/generators}/devise/templates/migration.rb +1 -1
- data/lib/generators/devise_install/devise_install_generator.rb +25 -0
- data/{generators → lib/generators}/devise_install/templates/README +4 -8
- data/{generators → lib/generators}/devise_install/templates/devise.rb +0 -3
- data/lib/generators/devise_views/devise_views_generator.rb +11 -0
- data/test/controllers/helpers_test.rb +15 -9
- data/test/devise_test.rb +1 -6
- data/test/encryptors_test.rb +0 -3
- data/test/failure_app_test.rb +6 -1
- data/test/integration/authenticatable_test.rb +25 -85
- data/test/integration/http_authenticatable_test.rb +2 -10
- data/test/integration/lockable_test.rb +3 -22
- data/test/integration/recoverable_test.rb +1 -1
- data/test/integration/registerable_test.rb +31 -36
- data/test/integration/rememberable_test.rb +6 -24
- data/test/integration/token_authenticatable_test.rb +2 -4
- data/test/integration/trackable_test.rb +1 -1
- data/test/mailers/confirmation_instructions_test.rb +4 -10
- data/test/mailers/unlock_instructions_test.rb +1 -1
- data/test/mapping_test.rb +12 -24
- data/test/models/authenticatable_test.rb +3 -3
- data/test/models/confirmable_test.rb +29 -29
- data/test/models/http_authenticatable_test.rb +19 -0
- data/test/models/lockable_test.rb +45 -44
- data/test/models/recoverable_test.rb +7 -7
- data/test/models/rememberable_test.rb +7 -10
- data/test/models/validatable_test.rb +19 -24
- data/test/models_test.rb +2 -16
- data/test/orm/active_record.rb +3 -4
- data/test/orm/mongo_mapper.rb +2 -10
- data/test/rails_app/app/active_record/admin.rb +1 -1
- data/test/rails_app/app/active_record/user.rb +3 -3
- data/test/rails_app/app/controllers/application_controller.rb +1 -7
- data/test/rails_app/app/controllers/sessions_controller.rb +6 -0
- data/test/rails_app/app/controllers/users_controller.rb +0 -4
- data/test/rails_app/app/mongo_mapper/admin.rb +4 -7
- data/test/rails_app/app/mongo_mapper/user.rb +5 -8
- data/test/rails_app/config/application.rb +32 -0
- data/test/rails_app/config/boot.rb +7 -108
- data/test/rails_app/config/environment.rb +4 -41
- data/test/rails_app/config/environments/development.rb +15 -13
- data/test/rails_app/config/environments/production.rb +25 -20
- data/test/rails_app/config/environments/test.rb +23 -22
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/cookie_verification_secret.rb +7 -0
- data/test/rails_app/config/initializers/devise.rb +0 -3
- data/test/rails_app/config/initializers/session_store.rb +2 -2
- data/test/rails_app/config/routes.rb +17 -21
- data/test/routes_test.rb +30 -47
- data/test/support/{assertions_helper.rb → assertions.rb} +0 -15
- data/test/support/{tests_helper.rb → helpers.rb} +16 -3
- data/test/support/{integration_tests_helper.rb → integration.rb} +8 -4
- data/test/support/webrat/integrations/rails.rb +31 -0
- data/test/test_helper.rb +8 -7
- data/test/test_helpers_test.rb +9 -9
- metadata +53 -128
- data/app/models/devise_mailer.rb +0 -68
- data/app/views/devise_mailer/confirmation_instructions.html.erb +0 -5
- data/app/views/devise_mailer/reset_password_instructions.html.erb +0 -8
- data/app/views/devise_mailer/unlock_instructions.html.erb +0 -7
- data/generators/devise/USAGE +0 -5
- data/generators/devise/devise_generator.rb +0 -15
- data/generators/devise/lib/route_devise.rb +0 -32
- data/generators/devise/templates/model.rb +0 -9
- data/generators/devise_install/USAGE +0 -3
- data/generators/devise_install/devise_install_generator.rb +0 -15
- data/generators/devise_views/USAGE +0 -3
- data/generators/devise_views/devise_views_generator.rb +0 -21
- data/rails/init.rb +0 -2
- data/test/integration/rack_middleware_test.rb +0 -47
- data/test/rails_app/config/initializers/new_rails_defaults.rb +0 -24
@@ -0,0 +1,35 @@
|
|
1
|
+
module Devise
|
2
|
+
module Controllers
|
3
|
+
module ScopedViews
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def scoped_views
|
8
|
+
defined?(@scoped_views) ? @scoped_views : Devise.scoped_views
|
9
|
+
end
|
10
|
+
|
11
|
+
def scoped_views=(value)
|
12
|
+
@scoped_views = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Render a view for the specified scope. Turned off by default.
|
19
|
+
# Accepts just :controller as option.
|
20
|
+
def render_with_scope(action, options={})
|
21
|
+
controller_name = options.delete(:controller) || self.controller_name
|
22
|
+
|
23
|
+
if self.class.scoped_views
|
24
|
+
begin
|
25
|
+
render :template => "#{devise_mapping.as}/#{controller_name}/#{action}"
|
26
|
+
rescue ActionView::MissingTemplate
|
27
|
+
render :template => "#{controller_path}/#{action}"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
render :template => "#{controller_path}/#{action}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/devise/failure_app.rb
CHANGED
@@ -22,8 +22,12 @@ module Devise
|
|
22
22
|
options = @env['warden.options']
|
23
23
|
scope = options[:scope]
|
24
24
|
|
25
|
-
redirect_path =
|
26
|
-
|
25
|
+
redirect_path = if mapping = Devise.mappings[scope]
|
26
|
+
"#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
|
27
|
+
else
|
28
|
+
"/#{default_url}"
|
29
|
+
end
|
30
|
+
query_string = query_string_for(options)
|
27
31
|
store_location!(scope)
|
28
32
|
|
29
33
|
headers = {}
|
@@ -50,23 +54,12 @@ module Devise
|
|
50
54
|
Rack::Utils.build_query(params)
|
51
55
|
end
|
52
56
|
|
53
|
-
# Build the path based on current scope.
|
54
|
-
def redirect_path_for(scope)
|
55
|
-
if mapping = Devise.mappings[scope]
|
56
|
-
"#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
|
57
|
-
else
|
58
|
-
"/#{default_url}"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
57
|
# Stores requested uri to redirect the user after signing in. We cannot use
|
63
58
|
# scoped session provided by warden here, since the user is not authenticated
|
64
59
|
# yet, but we still need to store the uri based on scope, so different scopes
|
65
60
|
# would never use the same uri to redirect.
|
66
61
|
def store_location!(scope)
|
67
|
-
|
68
|
-
session[:"#{scope}.return_to"] = request.request_uri
|
69
|
-
end
|
62
|
+
session[:"#{scope}.return_to"] = request.request_uri if request && request.get?
|
70
63
|
end
|
71
64
|
end
|
72
65
|
end
|
@@ -22,12 +22,9 @@ end
|
|
22
22
|
# Before logout hook to forget the user in the given scope, only if rememberable
|
23
23
|
# is activated for this scope. Also clear remember token to ensure the user
|
24
24
|
# won't be remembered again.
|
25
|
-
|
26
|
-
# user was just deleted.
|
27
|
-
Warden::Manager.before_logout do |record, warden, options|
|
28
|
-
scope = options[:scope]
|
25
|
+
Warden::Manager.before_logout do |record, warden, scope|
|
29
26
|
if record.respond_to?(:forget_me!)
|
30
|
-
record.forget_me!
|
31
|
-
warden.response.delete_cookie "remember_#{scope}_token"
|
27
|
+
record.forget_me!
|
28
|
+
warden.response.delete_cookie "remember_#{scope}_token"
|
32
29
|
end
|
33
30
|
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 :name, :as, :path_names, :path_prefix
|
25
|
+
attr_reader :name, :as, :path_names, :path_prefix
|
26
26
|
|
27
27
|
# Loop through all mappings looking for a map that matches with the requested
|
28
28
|
# path (ie /users/sign_in). If a path prefix is given, it's taken into account.
|
@@ -34,19 +34,26 @@ module Devise
|
|
34
34
|
nil
|
35
35
|
end
|
36
36
|
|
37
|
+
# Find a mapping by a given class. It takes into account single table inheritance as well.
|
38
|
+
def self.find_by_class(klass)
|
39
|
+
Devise.mappings.each_value do |mapping|
|
40
|
+
return mapping if klass <= mapping.to
|
41
|
+
end
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
37
45
|
# Receives an object and find a scope for it. If a scope cannot be found,
|
38
46
|
# raises an error. If a symbol is given, it's considered to be the scope.
|
39
47
|
def self.find_scope!(duck)
|
40
48
|
case duck
|
41
49
|
when String, Symbol
|
42
|
-
|
43
|
-
when Class
|
44
|
-
Devise.mappings.each_value { |m| return m.name if duck <= m.to }
|
50
|
+
duck
|
45
51
|
else
|
46
|
-
|
52
|
+
klass = duck.is_a?(Class) ? duck : duck.class
|
53
|
+
mapping = Devise::Mapping.find_by_class(klass)
|
54
|
+
raise "Could not find a valid mapping for #{duck}" unless mapping
|
55
|
+
mapping.name
|
47
56
|
end
|
48
|
-
|
49
|
-
raise "Could not find a valid mapping for #{duck}"
|
50
57
|
end
|
51
58
|
|
52
59
|
# Default url options which can be used as prefix.
|
@@ -59,13 +66,9 @@ module Devise
|
|
59
66
|
@klass = (options.delete(:class_name) || name.to_s.classify).to_s
|
60
67
|
@name = (options.delete(:scope) || name.to_s.singularize).to_sym
|
61
68
|
|
62
|
-
@path_prefix
|
63
|
-
@
|
64
|
-
|
65
|
-
@path_names = Hash.new { |h,k| h[k] = k.to_s }
|
69
|
+
@path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
|
70
|
+
@path_names = Hash.new { |h,k| h[k] = k.to_s }
|
66
71
|
@path_names.merge!(options.delete(:path_names) || {})
|
67
|
-
|
68
|
-
@sign_out_via = (options.delete(:sign_out_via) || :get)
|
69
72
|
end
|
70
73
|
|
71
74
|
# Return modules for the mapping.
|
@@ -98,17 +101,13 @@ module Devise
|
|
98
101
|
|
99
102
|
# Returns the parsed path taking into account the relative url root and raw path.
|
100
103
|
def parsed_path
|
101
|
-
(ActionController::Base.relative_url_root.to_s + raw_path)
|
104
|
+
returning (ActionController::Base.relative_url_root.to_s + raw_path) do |path|
|
102
105
|
self.class.default_url_options.each do |key, value|
|
103
106
|
path.gsub!(key.inspect, value.to_param)
|
104
107
|
end
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
108
|
-
def authenticatable?
|
109
|
-
@authenticatable ||= self.for.any? { |m| m.to_s =~ /authenticatable/ }
|
110
|
-
end
|
111
|
-
|
112
111
|
# Create magic predicates for verifying what module is activated by this map.
|
113
112
|
# Example:
|
114
113
|
#
|
data/lib/devise/models.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Devise
|
2
2
|
module Models
|
3
3
|
autoload :Activatable, 'devise/models/activatable'
|
4
|
-
autoload :
|
4
|
+
autoload :Authenticatable, 'devise/models/authenticatable'
|
5
5
|
autoload :Confirmable, 'devise/models/confirmable'
|
6
6
|
autoload :Lockable, 'devise/models/lockable'
|
7
7
|
autoload :Recoverable, 'devise/models/recoverable'
|
@@ -57,12 +57,7 @@ module Devise
|
|
57
57
|
#
|
58
58
|
def devise(*modules)
|
59
59
|
raise "You need to give at least one Devise module" if modules.empty?
|
60
|
-
options
|
61
|
-
|
62
|
-
if modules.delete(:authenticatable)
|
63
|
-
ActiveSupport::Deprecation.warn ":authenticatable as module is deprecated. Please give :database_authenticatable instead.", caller
|
64
|
-
modules << :database_authenticatable
|
65
|
-
end
|
60
|
+
options = modules.extract_options!
|
66
61
|
|
67
62
|
@devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
|
68
63
|
|
@@ -94,24 +89,13 @@ module Devise
|
|
94
89
|
if value.present?
|
95
90
|
record.send(:"#{attribute}=", value)
|
96
91
|
else
|
97
|
-
error
|
92
|
+
error = :blank
|
98
93
|
end
|
99
94
|
|
100
|
-
|
95
|
+
record.errors.add(attribute, error)
|
101
96
|
end
|
102
97
|
|
103
98
|
record
|
104
99
|
end
|
105
|
-
|
106
|
-
# Wraps add error logic in a method that works for different frameworks.
|
107
|
-
def add_error_on(record, attribute, error, add_default=true)
|
108
|
-
options = add_default ? { :default => error.to_s.gsub("_", " ") } : {}
|
109
|
-
|
110
|
-
begin
|
111
|
-
record.errors.add(attribute, error, options)
|
112
|
-
rescue ArgumentError
|
113
|
-
record.errors.add(attribute, error.to_s.gsub("_", " "))
|
114
|
-
end
|
115
|
-
end
|
116
100
|
end
|
117
101
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'devise/strategies/
|
1
|
+
require 'devise/strategies/authenticatable'
|
2
2
|
|
3
3
|
module Devise
|
4
4
|
module Models
|
@@ -26,20 +26,12 @@ module Devise
|
|
26
26
|
# User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
|
27
27
|
# User.find(1).valid_password?('password123') # returns true/false
|
28
28
|
#
|
29
|
-
module
|
30
|
-
|
31
|
-
base.class_eval do
|
32
|
-
extend ClassMethods
|
29
|
+
module Authenticatable
|
30
|
+
extend ActiveSupport::Concern
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
# TODO Remove me in next release
|
40
|
-
def old_password
|
41
|
-
ActiveSupport::Deprecation.warn "old_password is deprecated, please use current_password instead", caller
|
42
|
-
@old_password
|
32
|
+
included do
|
33
|
+
attr_reader :password, :current_password
|
34
|
+
attr_accessor :password_confirmation
|
43
35
|
end
|
44
36
|
|
45
37
|
# Regenerates password salt and encrypted password each time password is set,
|
@@ -55,7 +47,13 @@ module Devise
|
|
55
47
|
|
56
48
|
# Verifies whether an incoming_password (ie from sign in) is the user password.
|
57
49
|
def valid_password?(incoming_password)
|
58
|
-
|
50
|
+
password_digest(incoming_password) == self.encrypted_password
|
51
|
+
end
|
52
|
+
|
53
|
+
# Verifies whether an +incoming_authentication_token+ (i.e. from single access URL)
|
54
|
+
# is the user authentication token.
|
55
|
+
def valid_authentication_token?(incoming_auth_token)
|
56
|
+
incoming_auth_token == self.authentication_token
|
59
57
|
end
|
60
58
|
|
61
59
|
# Checks if a resource is valid upon authentication.
|
@@ -74,16 +72,13 @@ module Devise
|
|
74
72
|
def update_with_password(params={})
|
75
73
|
current_password = params.delete(:current_password)
|
76
74
|
|
77
|
-
if params[:password].blank?
|
78
|
-
|
79
|
-
params.delete(:password_confirmation) if params[:password_confirmation].blank?
|
80
|
-
end
|
75
|
+
params.delete(:password) if params[:password].blank?
|
76
|
+
params.delete(:password_confirmation) if params[:password_confirmation].blank?
|
81
77
|
|
82
78
|
result = if valid_password?(current_password)
|
83
79
|
update_attributes(params)
|
84
80
|
else
|
85
|
-
|
86
|
-
self.class.add_error_on(self, :current_password, message, false)
|
81
|
+
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
87
82
|
self.attributes = params
|
88
83
|
false
|
89
84
|
end
|
@@ -94,13 +89,6 @@ module Devise
|
|
94
89
|
|
95
90
|
protected
|
96
91
|
|
97
|
-
# Checks whether a password is needed or not. For validations only.
|
98
|
-
# Passwords are always required if it's a new record, or if the password
|
99
|
-
# or confirmation are being set somewhere.
|
100
|
-
def password_required?
|
101
|
-
new_record? || !password.nil? || !password_confirmation.nil?
|
102
|
-
end
|
103
|
-
|
104
92
|
# Digests the password using the configured encryptor.
|
105
93
|
def password_digest(password)
|
106
94
|
self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
|
@@ -29,15 +29,12 @@ module Devise
|
|
29
29
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
30
30
|
# User.find(1).resend_confirmation! # generates a new token and resent it
|
31
31
|
module Confirmable
|
32
|
+
extend ActiveSupport::Concern
|
32
33
|
include Devise::Models::Activatable
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
before_create :generate_confirmation_token, :if => :confirmation_required?
|
39
|
-
after_create :send_confirmation_instructions, :if => :confirmation_required?
|
40
|
-
end
|
35
|
+
included do
|
36
|
+
before_create :generate_confirmation_token, :if => :confirmation_required?
|
37
|
+
after_create :send_confirmation_instructions, :if => :confirmation_required?
|
41
38
|
end
|
42
39
|
|
43
40
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
@@ -46,7 +43,7 @@ module Devise
|
|
46
43
|
unless_confirmed do
|
47
44
|
self.confirmation_token = nil
|
48
45
|
self.confirmed_at = Time.now
|
49
|
-
save(false)
|
46
|
+
save(:validate => false)
|
50
47
|
end
|
51
48
|
end
|
52
49
|
|
@@ -57,13 +54,18 @@ module Devise
|
|
57
54
|
|
58
55
|
# Send confirmation instructions by email
|
59
56
|
def send_confirmation_instructions
|
60
|
-
|
61
|
-
::DeviseMailer.deliver_confirmation_instructions(self)
|
57
|
+
::Devise::Mailer.confirmation_instructions(self).deliver
|
62
58
|
end
|
63
59
|
|
64
|
-
#
|
65
|
-
|
66
|
-
|
60
|
+
# Remove confirmation date and send confirmation instructions, to ensure
|
61
|
+
# after sending these instructions the user won't be able to sign in without
|
62
|
+
# confirming it's account
|
63
|
+
def resend_confirmation!
|
64
|
+
unless_confirmed do
|
65
|
+
generate_confirmation_token
|
66
|
+
save(:validate => false)
|
67
|
+
send_confirmation_instructions
|
68
|
+
end
|
67
69
|
end
|
68
70
|
|
69
71
|
# Overwrites active? from Devise::Models::Activatable for confirmation
|
@@ -71,12 +73,16 @@ module Devise
|
|
71
73
|
# is already confirmed, it should never be blocked. Otherwise we need to
|
72
74
|
# calculate if the confirm time has not expired for this user.
|
73
75
|
def active?
|
74
|
-
super && (
|
76
|
+
super && (confirmed? || confirmation_period_valid?)
|
75
77
|
end
|
76
78
|
|
77
79
|
# The message to be shown if the account is inactive.
|
78
80
|
def inactive_message
|
79
|
-
!confirmed?
|
81
|
+
if !confirmed?
|
82
|
+
:unconfirmed
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
80
86
|
end
|
81
87
|
|
82
88
|
# If you don't want confirmation to be sent on create, neither a code
|
@@ -122,7 +128,7 @@ module Devise
|
|
122
128
|
unless confirmed?
|
123
129
|
yield
|
124
130
|
else
|
125
|
-
self.
|
131
|
+
self.errors.add(:email, :already_confirmed)
|
126
132
|
false
|
127
133
|
end
|
128
134
|
end
|
@@ -135,10 +141,6 @@ module Devise
|
|
135
141
|
self.confirmation_sent_at = Time.now.utc
|
136
142
|
end
|
137
143
|
|
138
|
-
def generate_confirmation_token!
|
139
|
-
generate_confirmation_token && save(false)
|
140
|
-
end
|
141
|
-
|
142
144
|
module ClassMethods
|
143
145
|
# Attempt to find a user by it's email. If a record is found, send new
|
144
146
|
# confirmation instructions to it. If not user is found, returns a new user
|
@@ -146,7 +148,7 @@ module Devise
|
|
146
148
|
# Options must contain the user email
|
147
149
|
def send_confirmation_instructions(attributes={})
|
148
150
|
confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
|
149
|
-
confirmable.
|
151
|
+
confirmable.resend_confirmation! unless confirmable.new_record?
|
150
152
|
confirmable
|
151
153
|
end
|
152
154
|
|
@@ -154,8 +156,8 @@ module Devise
|
|
154
156
|
# If no user is found, returns a new user with an error.
|
155
157
|
# If the user is already confirmed, create an error for the user
|
156
158
|
# Options must have the confirmation_token
|
157
|
-
def
|
158
|
-
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
159
|
+
def confirm!(attributes={})
|
160
|
+
confirmable = find_or_initialize_with_error_by(:confirmation_token, attributes[:confirmation_token])
|
159
161
|
confirmable.confirm! unless confirmable.new_record?
|
160
162
|
confirmable
|
161
163
|
end
|
@@ -3,16 +3,12 @@ require 'devise/strategies/http_authenticatable'
|
|
3
3
|
module Devise
|
4
4
|
module Models
|
5
5
|
# Adds HttpAuthenticatable behavior to your model. It expects that your
|
6
|
-
# model class responds to authenticate
|
7
|
-
# (which for example
|
6
|
+
# model class responds to authenticate and authentication_keys methods
|
7
|
+
# (which for example are defined in authenticatable).
|
8
8
|
module HttpAuthenticatable
|
9
|
-
|
10
|
-
base.extend ClassMethods
|
11
|
-
end
|
9
|
+
extend ActiveSupport::Concern
|
12
10
|
|
13
11
|
module ClassMethods
|
14
|
-
Devise::Models.config(self, :authentication_keys)
|
15
|
-
|
16
12
|
# Authenticate an user using http.
|
17
13
|
def authenticate_with_http(username, password)
|
18
14
|
authenticate(authentication_keys.first => username, :password => password)
|
@@ -18,62 +18,67 @@ module Devise
|
|
18
18
|
# available when unlock_strategy is :time or :both.
|
19
19
|
#
|
20
20
|
module Lockable
|
21
|
+
extend ActiveSupport::Concern
|
21
22
|
include Devise::Models::Activatable
|
22
23
|
|
23
|
-
def self.included(base)
|
24
|
-
base.class_eval do
|
25
|
-
extend ClassMethods
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
24
|
# Lock an user setting it's locked_at to actual time.
|
30
|
-
def
|
31
|
-
return true if access_locked?
|
25
|
+
def lock
|
32
26
|
self.locked_at = Time.now
|
33
|
-
|
34
|
-
if self.class.unlock_strategy_enabled?(:email)
|
27
|
+
if unlock_strategy_enabled?(:email)
|
35
28
|
generate_unlock_token
|
36
29
|
send_unlock_instructions
|
37
30
|
end
|
31
|
+
end
|
38
32
|
|
39
|
-
|
33
|
+
# Lock an user also saving the record.
|
34
|
+
def lock!
|
35
|
+
lock
|
36
|
+
save(:validate => false)
|
40
37
|
end
|
41
38
|
|
42
39
|
# Unlock an user by cleaning locket_at and failed_attempts.
|
43
|
-
def
|
44
|
-
|
40
|
+
def unlock!
|
41
|
+
if_locked do
|
45
42
|
self.locked_at = nil
|
46
43
|
self.failed_attempts = 0
|
47
44
|
self.unlock_token = nil
|
48
|
-
save(false)
|
45
|
+
save(:validate => false)
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
52
49
|
# Verifies whether a user is locked or not.
|
53
|
-
def
|
50
|
+
def locked?
|
54
51
|
locked_at && !lock_expired?
|
55
52
|
end
|
56
53
|
|
57
54
|
# Send unlock instructions by email
|
58
55
|
def send_unlock_instructions
|
59
|
-
::
|
56
|
+
::Devise::Mailer.unlock_instructions(self).deliver
|
60
57
|
end
|
61
58
|
|
62
59
|
# Resend the unlock instructions if the user is locked.
|
63
|
-
def
|
64
|
-
|
60
|
+
def resend_unlock!
|
61
|
+
if_locked do
|
62
|
+
generate_unlock_token unless unlock_token.present?
|
63
|
+
save(:validate => false)
|
64
|
+
send_unlock_instructions
|
65
|
+
end
|
65
66
|
end
|
66
67
|
|
67
68
|
# Overwrites active? from Devise::Models::Activatable for locking purposes
|
68
69
|
# by verifying whether an user is active to sign in or not based on locked?
|
69
70
|
def active?
|
70
|
-
super && !
|
71
|
+
super && !locked?
|
71
72
|
end
|
72
73
|
|
73
74
|
# Overwrites invalid_message from Devise::Models::Authenticatable to define
|
74
75
|
# the correct reason for blocking the sign in.
|
75
76
|
def inactive_message
|
76
|
-
|
77
|
+
if locked?
|
78
|
+
:locked
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
77
82
|
end
|
78
83
|
|
79
84
|
# Overwrites valid_for_authentication? from Devise::Models::Authenticatable
|
@@ -84,9 +89,9 @@ module Devise
|
|
84
89
|
self.failed_attempts = 0
|
85
90
|
else
|
86
91
|
self.failed_attempts += 1
|
87
|
-
|
92
|
+
lock if failed_attempts > self.class.maximum_attempts
|
88
93
|
end
|
89
|
-
save(false) if changed?
|
94
|
+
save(:validate => false) if changed?
|
90
95
|
result
|
91
96
|
end
|
92
97
|
|
@@ -99,7 +104,7 @@ module Devise
|
|
99
104
|
|
100
105
|
# Tells if the lock is expired if :time unlock strategy is active
|
101
106
|
def lock_expired?
|
102
|
-
if
|
107
|
+
if unlock_strategy_enabled?(:time)
|
103
108
|
locked_at && locked_at < self.class.unlock_in.ago
|
104
109
|
else
|
105
110
|
false
|
@@ -108,15 +113,20 @@ module Devise
|
|
108
113
|
|
109
114
|
# Checks whether the record is locked or not, yielding to the block
|
110
115
|
# if it's locked, otherwise adds an error to email.
|
111
|
-
def
|
112
|
-
if
|
116
|
+
def if_locked
|
117
|
+
if locked?
|
113
118
|
yield
|
114
119
|
else
|
115
|
-
self.
|
120
|
+
self.errors.add(:email, :not_locked)
|
116
121
|
false
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
125
|
+
# Is the unlock enabled for the given unlock strategy?
|
126
|
+
def unlock_strategy_enabled?(strategy)
|
127
|
+
[:both, strategy].include?(self.class.unlock_strategy)
|
128
|
+
end
|
129
|
+
|
120
130
|
module ClassMethods
|
121
131
|
# Attempt to find a user by it's email. If a record is found, send new
|
122
132
|
# unlock instructions to it. If not user is found, returns a new user
|
@@ -124,7 +134,7 @@ module Devise
|
|
124
134
|
# Options must contain the user email
|
125
135
|
def send_unlock_instructions(attributes={})
|
126
136
|
lockable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
|
127
|
-
lockable.
|
137
|
+
lockable.resend_unlock! unless lockable.new_record?
|
128
138
|
lockable
|
129
139
|
end
|
130
140
|
|
@@ -132,17 +142,12 @@ module Devise
|
|
132
142
|
# If no user is found, returns a new user with an error.
|
133
143
|
# If the user is not locked, creates an error for the user
|
134
144
|
# Options must have the unlock_token
|
135
|
-
def
|
136
|
-
lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
|
137
|
-
lockable.
|
145
|
+
def unlock!(attributes={})
|
146
|
+
lockable = find_or_initialize_with_error_by(:unlock_token, attributes[:unlock_token])
|
147
|
+
lockable.unlock! unless lockable.new_record?
|
138
148
|
lockable
|
139
149
|
end
|
140
150
|
|
141
|
-
# Is the unlock enabled for the given unlock strategy?
|
142
|
-
def unlock_strategy_enabled?(strategy)
|
143
|
-
[:both, strategy].include?(self.unlock_strategy)
|
144
|
-
end
|
145
|
-
|
146
151
|
Devise::Models.config(self, :maximum_attempts, :unlock_strategy, :unlock_in)
|
147
152
|
end
|
148
153
|
end
|