Skip to content
This repository has been archived by the owner on Jan 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #119 from equivalent/paranoid_verification
Browse files Browse the repository at this point in the history
reset code after several faild attempts
  • Loading branch information
traxanos committed Apr 7, 2015
2 parents 43dd886 + 48c04bb commit 5ee6e6b
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 5 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ add_index :the_resources, :expired_at
create_table :the_resources do |t|
# other devise fields

t.string :paranoid_verification_code
t.string :paranoid_verification_code
t.integer :paranoid_verification_attempt, default: 0
t.datetime :paranoid_verified_at
end
add_index :the_resources, :paranoid_verification_code
Expand Down
2 changes: 2 additions & 0 deletions app/views/devise/paranoid_verification_code/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
<p><%= f.label :paranoid_verification_code, 'Verification code' %><br />
<%= f.text_field :paranoid_verification_code, value: '' %></p>

<p>After <strong><%= resource.paranoid_attempts_remaining %></strong> faild attemtps, code will be regenerated<p>

<p><%= f.submit "Submit" %></p>
<% end %>
4 changes: 4 additions & 0 deletions lib/devise_security_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ module Devise
@@expire_after = 90.days
mattr_accessor :delete_expired_after
@@delete_expired_after = 90.days

# paranoid_verification will regenerate verifacation code after faild attempt
mattr_accessor :paranoid_code_regenerate_after_attempt
@@paranoid_code_regenerate_after_attempt = 10
end

# an security extension for devise
Expand Down
18 changes: 14 additions & 4 deletions lib/devise_security_extension/models/paranoid_verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

module Devise
module Models

# PasswordExpirable takes care of change password after
module ParanoidVerification
extend ActiveSupport::Concern
Expand All @@ -12,13 +11,24 @@ def need_paranoid_verification?
end

def verify_code(code)
if code == paranoid_verification_code
update_without_password paranoid_verification_code: nil, paranoid_verified_at: Time.now
attempt = paranoid_verification_attempt

if (attempt += 1) >= Devise.paranoid_code_regenerate_after_attempt
generate_paranoid_code
elsif code == paranoid_verification_code
attempt = 0
update_without_password paranoid_verification_code: nil, paranoid_verified_at: Time.now, paranoid_verification_attempt: attempt
else
update_without_password paranoid_verification_attempt: attempt
end
end

def paranoid_attempts_remaining
Devise.paranoid_code_regenerate_after_attempt - paranoid_verification_attempt
end

def generate_paranoid_code
update_without_password paranoid_verification_code: Devise.verification_code_generator.call()
update_without_password paranoid_verification_code: Devise.verification_code_generator.call(), paranoid_verification_attempt: 0
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddVerificationAttemptColumn < ActiveRecord::Migration
def self.up
add_column :users, :paranoid_verification_attempt, :integer, default: 0
end

def self.down
remove_column :users, :paranoid_verification_attempt
end
end
68 changes: 68 additions & 0 deletions test/test_paranoid_verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ class TestPasswordVerifiable < ActiveSupport::TestCase
end

test "generate code" do
user = User.new
user.generate_paranoid_code
assert_equal(0, user.paranoid_verification_attempt)
user.verify_code('wrong')
assert_equal(1, user.paranoid_verification_attempt)
user.generate_paranoid_code
assert_equal(0, user.paranoid_verification_attempt)
end

test "generate code must reset attempt counter" do
user = User.new
user.generate_paranoid_code
# default generator generates 5 char string
Expand Down Expand Up @@ -54,4 +64,62 @@ class TestPasswordVerifiable < ActiveSupport::TestCase
user.verify_code('wrong')
assert_equal(nil, user.paranoid_verified_at)
end

test 'when code not match upon verification code too many attempts should generate new code' do
original_regenerate = Devise.paranoid_code_regenerate_after_attempt
Devise.paranoid_code_regenerate_after_attempt = 2

user = User.create(paranoid_verification_code: 'abcde')
user.verify_code('wrong')
assert_equal 'abcde', user.paranoid_verification_code
user.verify_code('wrong-again')
assert_not_equal 'abcde', user.paranoid_verification_code

Devise.paranoid_code_regenerate_after_attempt = original_regenerate
end

test 'upon generating new code due to too many attempts reset attempt counter' do
original_regenerate = Devise.paranoid_code_regenerate_after_attempt
Devise.paranoid_code_regenerate_after_attempt = 3

user = User.create(paranoid_verification_code: 'abcde')
user.verify_code('wrong')
assert_equal 1, user.paranoid_verification_attempt
user.verify_code('wrong-again')
assert_equal 2, user.paranoid_verification_attempt
user.verify_code('WRONG!')
assert_equal 0, user.paranoid_verification_attempt

Devise.paranoid_code_regenerate_after_attempt = original_regenerate
end


test 'by default paranoid code regenerate should have 10 attempts' do
user = User.new(paranoid_verification_code: 'abcde')
assert_equal 10, user.paranoid_attempts_remaining
end

test 'paranoid_attempts_remaining should re-callculate how many attemps remains after each wrong attempt' do
original_regenerate = Devise.paranoid_code_regenerate_after_attempt
Devise.paranoid_code_regenerate_after_attempt = 2

user = User.create(paranoid_verification_code: 'abcde')
assert_equal 2, user.paranoid_attempts_remaining

user.verify_code('WRONG!')
assert_equal 1, user.paranoid_attempts_remaining

Devise.paranoid_code_regenerate_after_attempt = original_regenerate
end

test 'when code not match upon verification code too many times, reset paranoid_attempts_remaining' do
original_regenerate = Devise.paranoid_code_regenerate_after_attempt
Devise.paranoid_code_regenerate_after_attempt = 1

user = User.create(paranoid_verification_code: 'abcde')
user.verify_code('wrong') # at this point code was regenerated
assert_equal Devise.paranoid_code_regenerate_after_attempt, user.paranoid_attempts_remaining

Devise.paranoid_code_regenerate_after_attempt = original_regenerate
end
end

0 comments on commit 5ee6e6b

Please sign in to comment.