Skip to content

Commit c09e217

Browse files
committed
Add additional step for unlocking account
1 parent cf93de3 commit c09e217

File tree

13 files changed

+105
-24
lines changed

13 files changed

+105
-24
lines changed

app/controllers/devise/unlocks_controller.rb

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,38 @@ def create
2222

2323
# GET /resource/unlock?unlock_token=abcdef
2424
def show
25+
if resource_class.extra_step
26+
@token = params[:unlock_token]
27+
render :show
28+
else
29+
attempt_unlock
30+
end
31+
end
32+
33+
# GET /resource/unlock/confirm?unlock_token=abcdef
34+
def confirm
35+
attempt_unlock
36+
end
37+
38+
protected
39+
40+
# The path used after sending unlock password instructions
41+
def after_sending_unlock_instructions_path_for(resource)
42+
new_session_path(resource) if is_navigational_format?
43+
end
44+
45+
# The path used after unlocking the resource
46+
def after_unlock_path_for(resource)
47+
new_session_path(resource) if is_navigational_format?
48+
end
49+
50+
def translation_scope
51+
'devise.unlocks'
52+
end
53+
54+
private
55+
56+
def attempt_unlock
2557
self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
2658
yield resource if block_given?
2759

@@ -33,20 +65,4 @@ def show
3365
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
3466
end
3567
end
36-
37-
protected
38-
39-
# The path used after sending unlock password instructions
40-
def after_sending_unlock_instructions_path_for(resource)
41-
new_session_path(resource) if is_navigational_format?
42-
end
43-
44-
# The path used after unlocking the resource
45-
def after_unlock_path_for(resource)
46-
new_session_path(resource) if is_navigational_format?
47-
end
48-
49-
def translation_scope
50-
'devise.unlocks'
51-
end
5268
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<h2>Confirm unlock</h2>
2+
3+
<p>Click the link below to finish unlocking your account</p>
4+
5+
<p><%= link_to "Unlock account", confirm_unlock_path(resource_name, unlock_token: @token) %><p>
6+
7+
<%= render "devise/shared/links" %>

lib/devise.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ module Test
192192
mattr_accessor :unlock_in
193193
@@unlock_in = 1.hour
194194

195+
# Defines if an extra step is used to unlock an account
196+
mattr_accessor :extra_step
197+
@@extra_step = false
198+
195199
# Defines which key will be used when recovering the password for an account
196200
mattr_accessor :reset_password_keys
197201
@@reset_password_keys = [:email]

lib/devise/models/lockable.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module Models
2020
# * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
2121
# * +unlock_in+: the time you want to unlock the user after lock happens. Only available when unlock_strategy is :time or :both.
2222
# * +unlock_keys+: the keys you want to use when locking and unlocking an account
23+
# * +extra_step+: if an extra step is used to unlock an account
2324
#
2425
module Lockable
2526
extend ActiveSupport::Concern
@@ -207,7 +208,7 @@ def lock_strategy_enabled?(strategy)
207208
self.lock_strategy == strategy
208209
end
209210

210-
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
211+
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning, :extra_step)
211212
end
212213
end
213214
end

lib/devise/modules.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
# The ones which can sign out after
2323
routes = [nil, :new]
2424
d.add_module :confirmable, controller: :confirmations, route: { confirmation: routes }
25-
d.add_module :lockable, controller: :unlocks, route: { unlock: routes }
25+
d.add_module :lockable, controller: :unlocks, route: { unlock: (routes << :confirm) }
2626
d.add_module :timeoutable
2727

2828
# Stats for last, so we make sure the user is really signed in

lib/devise/rails/routes.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,15 @@ def devise_confirmation(mapping, controllers) #:nodoc:
393393

394394
def devise_unlock(mapping, controllers) #:nodoc:
395395
if mapping.to.unlock_strategy_enabled?(:email)
396-
resource :unlock, only: [:new, :create, :show],
397-
path: mapping.path_names[:unlock], controller: controllers[:unlocks]
396+
options = {
397+
only: [:new, :create, :show],
398+
path: mapping.path_names[:unlock],
399+
controller: controllers[:unlocks]
400+
}
401+
402+
resource :unlock, **options do
403+
get :confirm
404+
end
398405
end
399406
end
400407

lib/generators/templates/controllers/unlocks_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class <%= @scope_prefix %>UnlocksController < Devise::UnlocksController
1616
# super
1717
# end
1818

19+
# GET /resource/unlock/confirm?unlock_token=abcdef
20+
# def confirm
21+
# super
22+
# end
23+
1924
# protected
2025

2126
# The path used after sending unlock password instructions

lib/generators/templates/devise.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@
216216
# Warn on the last attempt before the account is locked.
217217
# config.last_attempt_warning = true
218218

219+
# Defines if an extra step is used to unlock an account
220+
# config.extra_step = false
221+
219222
# ==> Configuration for :recoverable
220223
#
221224
# Defines which key will be used when recovering the password for an account
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<h2>Confirm unlock</h2>
2+
3+
<p>Click the link below to finish unlocking your account</p>
4+
5+
<p><%= link_to "Unlock account", confirm_unlock_path(resource_name, unlock_token: @token) %><p>
6+
7+
<%= render "devise/shared/links" %>

test/generators/views_generator_test.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
4141
assert_files nil, mail_template_engine: "markerb"
4242
end
4343

44-
4544
test "Assert only views within specified directories" do
4645
run_generator %w(-v sessions registrations)
4746
assert_file "app/views/devise/sessions/new.html.erb"
@@ -68,7 +67,7 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
6867
run_generator %w(-v registrations -b simple_form_for)
6968
assert_file "app/views/devise/registrations/new.html.erb", /simple_form_for/
7069
assert_no_file "app/views/devise/confirmations/new.html.erb"
71-
end
70+
end
7271

7372
test "Assert specified directories with markerb" do
7473
run_generator %w(--markerb -v passwords mailer)
@@ -93,6 +92,7 @@ def assert_files(scope = nil, options = {})
9392
assert_file "app/views/#{scope}/shared/_links.html.erb"
9493
assert_file "app/views/#{scope}/shared/_error_messages.html.erb"
9594
assert_file "app/views/#{scope}/unlocks/new.html.erb"
95+
assert_file "app/views/#{scope}/unlocks/show.html.erb"
9696
end
9797

9898
def assert_shared_links(scope = nil)
@@ -105,6 +105,7 @@ def assert_shared_links(scope = nil)
105105
assert_file "app/views/#{scope}/registrations/new.html.erb", link
106106
assert_file "app/views/#{scope}/sessions/new.html.erb", link
107107
assert_file "app/views/#{scope}/unlocks/new.html.erb", link
108+
assert_file "app/views/#{scope}/unlocks/show.html.erb", link
108109
end
109110

110111
def assert_error_messages(scope = nil)

0 commit comments

Comments
 (0)