devise-authy 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/README.md +22 -1
  4. data/VERSION +1 -1
  5. data/app/controllers/devise/devise_authy_controller.rb +25 -12
  6. data/app/controllers/devise_authy/passwords_controller.rb +2 -2
  7. data/authy-devise-demo/Gemfile.lock +4 -4
  8. data/authy-devise-demo/app/controllers/welcome_controller.rb +10 -1
  9. data/authy-devise-demo/app/models/admin.rb +10 -0
  10. data/authy-devise-demo/app/models/user.rb +3 -4
  11. data/authy-devise-demo/app/views/welcome/admin_page.html.erb +12 -0
  12. data/authy-devise-demo/app/views/welcome/index.html.erb +2 -7
  13. data/authy-devise-demo/app/views/welcome/user_page.html.erb +5 -0
  14. data/authy-devise-demo/config/initializers/devise.rb +3 -3
  15. data/authy-devise-demo/config/locales/devise.authy.en.yml +2 -2
  16. data/authy-devise-demo/config/routes.rb +4 -1
  17. data/authy-devise-demo/db/migrate/20130409234357_devise_create_users.rb +3 -3
  18. data/authy-devise-demo/db/migrate/20141202000744_devise_create_admins.rb +46 -0
  19. data/authy-devise-demo/db/migrate/20141202004246_devise_authy_add_to_admins.rb +21 -0
  20. data/authy-devise-demo/db/schema.rb +27 -3
  21. data/devise-authy.gemspec +12 -4
  22. data/lib/devise-authy.rb +2 -0
  23. data/lib/devise-authy/controllers/helpers.rb +7 -3
  24. data/lib/devise-authy/models/authy_lockable.rb +43 -0
  25. data/spec/controllers/devise_authy_controller_spec.rb +40 -0
  26. data/spec/features/authy_authenticatable_spec.rb +0 -5
  27. data/spec/features/authy_lockable_spec.rb +70 -0
  28. data/spec/models/{authy_authenticatable.rb → authy_authenticatable_spec.rb} +0 -0
  29. data/spec/models/authy_lockable_spec.rb +81 -0
  30. data/spec/rails-app/app/controllers/welcome_controller.rb +8 -1
  31. data/spec/rails-app/app/models/lockable_user.rb +7 -0
  32. data/spec/rails-app/config/initializers/devise.rb +4 -4
  33. data/spec/rails-app/config/routes.rb +1 -0
  34. data/spec/rails-app/db/development.sqlite3 +0 -0
  35. data/spec/rails-app/db/migrate/20130419164907_devise_create_users.rb +3 -3
  36. data/spec/rails-app/db/schema.rb +3 -0
  37. data/spec/support/helpers.rb +48 -3
  38. metadata +12 -4
  39. data/authy-devise-demo/db/migrate/20130409234434_devise_authy_add_to_users.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a567fa8bbad855383c29a5bda348dbeb6d57b63
4
- data.tar.gz: 5157b30f2294c1c5f07cba33b538daf4b137efa5
3
+ metadata.gz: e459c469815b8231e8134201d93fce4549dae9a6
4
+ data.tar.gz: a049bec15607162733009e40118b738596f476ae
5
5
  SHA512:
6
- metadata.gz: 796c3197ca44d7658a11bb7cf0d67aebdbce65f83c40e2cc6fb9dfeeddffa8844f73476edefb4e05d13d3fa7799c2088c94daedd42112d1709b0861f54f684a5
7
- data.tar.gz: ba9c039d84e42c35bfec5edeb1a9ac8c5ee1a29cff04f4505521ac7a42cdb276a194ba193b5685ec2f53d194758844b835dc5ad23934458bac861b41f3125e6d
6
+ metadata.gz: ac16e44154437ba153eba18c70960b49f49f80de71f11e9fb3784d2b4ec2a0e5a55661379d92fa72722566966e8337073b27341384db9c3d3c14a198772802d7
7
+ data.tar.gz: c826d6ef69433cff85a45d79a0267c922d5f7ee8e59233347ecaa5d4758dd9d32b2b07152d945e9912e2d8910a41985ceb01011c7d4c9180c5e79dea1fd096ed
@@ -30,7 +30,7 @@ GEM
30
30
  multi_json (~> 1.0)
31
31
  addressable (2.3.6)
32
32
  arel (3.0.2)
33
- authy (2.3.1)
33
+ authy (2.4.0)
34
34
  httpclient (>= 2.3.4)
35
35
  bcrypt-ruby (3.0.1)
36
36
  builder (3.0.4)
@@ -62,7 +62,7 @@ GEM
62
62
  hashie (2.1.1)
63
63
  highline (1.6.21)
64
64
  hike (1.2.2)
65
- httpclient (2.3.4.1)
65
+ httpclient (2.5.3.3)
66
66
  i18n (0.6.1)
67
67
  jeweler (2.0.1)
68
68
  builder
data/README.md CHANGED
@@ -13,7 +13,7 @@ See [https://github.com/authy/authy-devise/tree/master/authy-devise-demo](https:
13
13
 
14
14
  ## Getting started
15
15
 
16
- First create an initializer in `config/initializer/authy.rb`
16
+ First create an initializer in `config/initializers/authy.rb`
17
17
 
18
18
  ```ruby
19
19
  Authy.api_key = ENV['AUTHY_API_KEY'] || 'your_authy_api_key'
@@ -105,6 +105,12 @@ class MyCustomModule::DeviseAuthyController < Devise::DeviseAuthyController
105
105
  end
106
106
  ```
107
107
 
108
+ And tell the router to use this controller
109
+
110
+ ```ruby
111
+ devise_for :users, controllers: {devise_authy: 'my_custom_module/devise_authy'}
112
+ ```
113
+
108
114
 
109
115
  ## I18n
110
116
 
@@ -112,6 +118,21 @@ The install generator also copy a `Devise Authy` i18n file which you can find at
112
118
 
113
119
  config/locales/devise.authy.en.yml
114
120
 
121
+
122
+ ## Running Tests
123
+
124
+ To prepare the tests run the following commands:
125
+ ```bash
126
+ $ cd spec/rails-app
127
+ $ bundle install
128
+ $ RAILS_ENV=test bundle exec rake db:migrate
129
+ ```
130
+
131
+ Now on the project root run the following commands:
132
+ ```bash
133
+ $ bundle exec rspec spec/
134
+ ```
135
+
115
136
  ## Copyright
116
137
 
117
138
  Copyright (c) 2014 Authy Inc. See LICENSE.txt for
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.3
1
+ 1.6.0
@@ -37,8 +37,7 @@ class Devise::DeviseAuthyController < DeviseController
37
37
  set_flash_message(:notice, :signed_in) if is_navigational_format?
38
38
  respond_with resource, :location => after_sign_in_path_for(@resource)
39
39
  else
40
- set_flash_message(:error, :invalid_token)
41
- render :verify_authy
40
+ handle_invalid_token :verify_authy, :invalid_token
42
41
  end
43
42
  end
44
43
 
@@ -103,9 +102,9 @@ class Devise::DeviseAuthyController < DeviseController
103
102
  })
104
103
 
105
104
  self.resource.authy_enabled = token.ok?
105
+
106
106
  if !token.ok? || !self.resource.save
107
- set_flash_message(:error, :not_enabled)
108
- render :verify_authy_installation
107
+ handle_invalid_token :verify_authy_installation, :not_enabled
109
108
  else
110
109
  set_flash_message(:notice, :enabled)
111
110
  redirect_to after_authy_verified_path_for(resource)
@@ -127,6 +126,7 @@ class Devise::DeviseAuthyController < DeviseController
127
126
  def authenticate_scope!
128
127
  send(:"authenticate_#{resource_name}!", :force => true)
129
128
  self.resource = send("current_#{resource_name}")
129
+ @resource = resource
130
130
  end
131
131
 
132
132
  def find_resource
@@ -147,15 +147,28 @@ class Devise::DeviseAuthyController < DeviseController
147
147
 
148
148
  protected
149
149
 
150
- def after_authy_enabled_path_for(resource)
151
- root_path
152
- end
150
+ def after_authy_enabled_path_for(resource)
151
+ root_path
152
+ end
153
153
 
154
- def after_authy_verified_path_for(resource)
155
- after_authy_enabled_path_for(resource)
156
- end
154
+ def after_authy_verified_path_for(resource)
155
+ after_authy_enabled_path_for(resource)
156
+ end
157
157
 
158
- def invalid_resource_path
159
- root_path
158
+ def invalid_resource_path
159
+ root_path
160
+ end
161
+
162
+ def handle_invalid_token(view, error_message)
163
+ if @resource.respond_to?(:invalid_authy_attempt!) && @resource.invalid_authy_attempt!
164
+ after_account_is_locked
165
+ else
166
+ set_flash_message(:error, error_message)
167
+ render view
160
168
  end
169
+ end
170
+
171
+ def after_account_is_locked
172
+ sign_out_and_redirect @resource
173
+ end
161
174
  end
@@ -2,11 +2,11 @@ class DeviseAuthy::PasswordsController < Devise::PasswordsController
2
2
  def sign_in(resource_or_scope, *args)
3
3
  resource = args.last || resource_or_scope
4
4
 
5
- if resource.with_authy_authentication?(request)
5
+ if resource.respond_to?(:with_authy_authentication?) && resource.with_authy_authentication?(request)
6
6
  # Do nothing. Because we need verify the 2FA
7
7
  true
8
8
  else
9
9
  super
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- devise-authy (1.5.2)
4
+ devise-authy (1.5.3)
5
5
  authy
6
6
  devise
7
7
 
@@ -36,7 +36,7 @@ GEM
36
36
  i18n (= 0.6.1)
37
37
  multi_json (~> 1.0)
38
38
  arel (3.0.2)
39
- authy (2.3.1)
39
+ authy (2.4.0)
40
40
  httpclient (>= 2.3.4)
41
41
  bcrypt-ruby (3.0.1)
42
42
  builder (3.0.4)
@@ -57,7 +57,7 @@ GEM
57
57
  execjs (1.4.0)
58
58
  multi_json (~> 1.0)
59
59
  hike (1.2.2)
60
- httpclient (2.3.4.1)
60
+ httpclient (2.5.3.3)
61
61
  i18n (0.6.1)
62
62
  journey (1.0.4)
63
63
  jquery-rails (2.2.1)
@@ -80,7 +80,7 @@ GEM
80
80
  rack (1.4.5)
81
81
  rack-cache (1.2)
82
82
  rack (>= 0.4)
83
- rack-ssl (1.3.3)
83
+ rack-ssl (1.3.4)
84
84
  rack
85
85
  rack-test (0.6.2)
86
86
  rack (>= 1.0)
@@ -1,6 +1,15 @@
1
1
  class WelcomeController < ApplicationController
2
- before_filter :authenticate_user!
2
+ before_filter :authenticate_user!, only: "user_page"
3
+ before_filter :authenticate_admin!, only: "admin_page"
3
4
 
4
5
  def index
6
+ redirect_to welcome_admin_page_path if current_admin
7
+ redirect_to welcome_user_page_path if current_user
8
+ end
9
+
10
+ def admin_page
11
+ end
12
+
13
+ def user_page
5
14
  end
6
15
  end
@@ -0,0 +1,10 @@
1
+ class Admin < ActiveRecord::Base
2
+ # Include default devise modules. Others available are:
3
+ # :token_authenticatable, :confirmable,
4
+ # :lockable, :timeoutable and :omniauthable
5
+ devise :authy_authenticatable, :authy_lockable, :database_authenticatable, :registerable,
6
+ :recoverable, :rememberable, :trackable, :validatable, :lockable
7
+
8
+ # Setup accessible (or protected) attributes for your model
9
+ attr_accessible :authy_id, :last_sign_in_with_authy, :email, :password, :password_confirmation, :remember_me
10
+ end
@@ -2,10 +2,9 @@ class User < ActiveRecord::Base
2
2
  # Include default devise modules. Others available are:
3
3
  # :token_authenticatable, :confirmable,
4
4
  # :lockable, :timeoutable and :omniauthable
5
- devise :authy_authenticatable, :database_authenticatable, :registerable,
6
- :recoverable, :rememberable, :trackable, :validatable
5
+ devise :database_authenticatable, :registerable,
6
+ :recoverable, :rememberable, :trackable, :validatable, :lockable
7
7
 
8
8
  # Setup accessible (or protected) attributes for your model
9
- attr_accessible :authy_id, :last_sign_in_with_authy, :email, :password, :password_confirmation, :remember_me
10
- # attr_accessible :authy_id, :last_sign_in_with_authy, :title, :body
9
+ attr_accessible :email, :password, :password_confirmation, :remember_me
11
10
  end
@@ -0,0 +1,12 @@
1
+ <h1>Welcome#admin_page</h1>
2
+ <p>Find me in app/views/welcome/admin_page.html.erb</p>
3
+
4
+
5
+ <% if current_admin.authy_enabled %>
6
+ <%= link_to "Disable authy", admin_disable_authy_path, :method => :post %>
7
+ <% else %>
8
+ <%= link_to "Enable authy", admin_enable_authy_path %>
9
+ <% end %>
10
+
11
+ <%= link_to "Logout", destroy_admin_session_path, :method => :delete %>
12
+
@@ -1,10 +1,5 @@
1
1
  <h1>Welcome#index</h1>
2
2
  <p>Find me in app/views/welcome/index.html.erb</p>
3
3
 
4
- <% if current_user.authy_enabled %>
5
- <%= link_to "Disable authy", user_disable_authy_path, :method => :post %>
6
- <% else %>
7
- <%= link_to "Enable authy", user_enable_authy_path %>
8
- <% end %>
9
-
10
- <%= link_to "Logout", destroy_user_session_path, :method => :delete %>
4
+ <%= link_to "User Login", new_user_session_path %>
5
+ <%= link_to "Admin Login", new_admin_session_path %>
@@ -0,0 +1,5 @@
1
+ <h1>Welcome#user_page</h1>
2
+ <p>Find me in app/views/welcome/user_page.html.erb</p>
3
+
4
+ <%= link_to "Logout", destroy_user_session_path, :method => :delete %>
5
+
@@ -143,14 +143,14 @@ Devise.setup do |config|
143
143
  # :time = Re-enables login after a certain amount of time (see :unlock_in below)
144
144
  # :both = Enables both strategies
145
145
  # :none = No unlock strategy. You should handle unlocking by yourself.
146
- # config.unlock_strategy = :both
146
+ config.unlock_strategy = :time
147
147
 
148
148
  # Number of authentication tries before locking an account if lock_strategy
149
149
  # is failed attempts.
150
- # config.maximum_attempts = 20
150
+ config.maximum_attempts = 20
151
151
 
152
152
  # Time interval to unlock the account if :time is enabled as unlock_strategy.
153
- # config.unlock_in = 1.hour
153
+ config.unlock_in = 1.hour
154
154
 
155
155
  # ==> Configuration for :recoverable
156
156
  #
@@ -13,9 +13,9 @@ en:
13
13
  enable_my_account: 'Enable my account'
14
14
 
15
15
  devise_authy:
16
- user:
16
+ admin:
17
17
  enabled: 'Two factor authentication was enabled'
18
18
  not_enabled: 'Something went wrong while enabling two factor authentication'
19
19
  signed_in: 'Signed in with Authy successfully.'
20
20
  already_enabled: "Two factor authentication is already enabled."
21
- invalid_token: 'The entered token is invalid.'
21
+ invalid_token: 'The entered token is invalid.'
@@ -1,7 +1,10 @@
1
1
  AuthyDeviseDemo::Application.routes.draw do
2
+ devise_for :admins
2
3
  devise_for :users
3
4
 
4
5
  get "welcome/index"
6
+ get "welcome/user_page"
7
+ get "welcome/admin_page"
5
8
 
6
9
  # The priority is based upon order of creation:
7
10
  # first created -> highest priority.
@@ -52,7 +55,7 @@ AuthyDeviseDemo::Application.routes.draw do
52
55
 
53
56
  # You can have the root of your site routed with "root"
54
57
  # just remember to delete public/index.html.
55
- root :to => 'welcome#index'
58
+ root :to => 'welcome#index'
56
59
 
57
60
  # See how all your routes lay out with "rake routes"
58
61
 
@@ -26,9 +26,9 @@ class DeviseCreateUsers < ActiveRecord::Migration
26
26
  # t.string :unconfirmed_email # Only if using reconfirmable
27
27
 
28
28
  ## Lockable
29
- # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30
- # t.string :unlock_token # Only if unlock strategy is :email or :both
31
- # t.datetime :locked_at
29
+ t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30
+ t.string :unlock_token # Only if unlock strategy is :email or :both
31
+ t.datetime :locked_at
32
32
 
33
33
  ## Token authenticatable
34
34
  # t.string :authentication_token
@@ -0,0 +1,46 @@
1
+ class DeviseCreateAdmins < ActiveRecord::Migration
2
+ def change
3
+ create_table(:admins) do |t|
4
+ ## Database authenticatable
5
+ t.string :email, :null => false, :default => ""
6
+ t.string :encrypted_password, :null => false, :default => ""
7
+
8
+ ## Recoverable
9
+ t.string :reset_password_token
10
+ t.datetime :reset_password_sent_at
11
+
12
+ ## Rememberable
13
+ t.datetime :remember_created_at
14
+
15
+ ## Trackable
16
+ t.integer :sign_in_count, :default => 0
17
+ t.datetime :current_sign_in_at
18
+ t.datetime :last_sign_in_at
19
+ t.string :current_sign_in_ip
20
+ t.string :last_sign_in_ip
21
+
22
+ ## Confirmable
23
+ # t.string :confirmation_token
24
+ # t.datetime :confirmed_at
25
+ # t.datetime :confirmation_sent_at
26
+ # t.string :unconfirmed_email # Only if using reconfirmable
27
+
28
+ ## Lockable
29
+ # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30
+ # t.string :unlock_token # Only if unlock strategy is :email or :both
31
+ # t.datetime :locked_at
32
+
33
+ ## Token authenticatable
34
+ # t.string :authentication_token
35
+
36
+
37
+ t.timestamps
38
+ end
39
+
40
+ add_index :admins, :email, :unique => true
41
+ add_index :admins, :reset_password_token, :unique => true
42
+ # add_index :admins, :confirmation_token, :unique => true
43
+ # add_index :admins, :unlock_token, :unique => true
44
+ # add_index :admins, :authentication_token, :unique => true
45
+ end
46
+ end
@@ -0,0 +1,21 @@
1
+ class DeviseAuthyAddToAdmins < ActiveRecord::Migration
2
+ def self.up
3
+ change_table :admins do |t|
4
+ t.string :authy_id
5
+ t.datetime :last_sign_in_with_authy
6
+ t.boolean :authy_enabled, :default => false
7
+ t.integer :failed_attempts, :default => 0
8
+ t.string :unlock_token
9
+ t.datetime :locked_at
10
+ end
11
+
12
+ add_index :admins, :authy_id
13
+ end
14
+
15
+ def self.down
16
+ change_table :admins do |t|
17
+ t.remove :authy_id, :last_sign_in_with_authy, :authy_enabled, :failed_attempts, :unlock_token, :locked_at
18
+ end
19
+ end
20
+ end
21
+
@@ -11,9 +11,9 @@
11
11
  #
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20130409234434) do
14
+ ActiveRecord::Schema.define(:version => 20141202004246) do
15
15
 
16
- create_table "users", :force => true do |t|
16
+ create_table "admins", :force => true do |t|
17
17
  t.string "email", :default => "", :null => false
18
18
  t.string "encrypted_password", :default => "", :null => false
19
19
  t.string "reset_password_token"
@@ -29,9 +29,33 @@ ActiveRecord::Schema.define(:version => 20130409234434) do
29
29
  t.string "authy_id"
30
30
  t.datetime "last_sign_in_with_authy"
31
31
  t.boolean "authy_enabled", :default => false
32
+ t.integer "failed_attempts", :default => 0
33
+ t.string "unlock_token"
34
+ t.datetime "locked_at"
35
+ end
36
+
37
+ add_index "admins", ["authy_id"], :name => "index_admins_on_authy_id"
38
+ add_index "admins", ["email"], :name => "index_admins_on_email", :unique => true
39
+ add_index "admins", ["reset_password_token"], :name => "index_admins_on_reset_password_token", :unique => true
40
+
41
+ create_table "users", :force => true do |t|
42
+ t.string "email", :default => "", :null => false
43
+ t.string "encrypted_password", :default => "", :null => false
44
+ t.string "reset_password_token"
45
+ t.datetime "reset_password_sent_at"
46
+ t.datetime "remember_created_at"
47
+ t.integer "sign_in_count", :default => 0
48
+ t.datetime "current_sign_in_at"
49
+ t.datetime "last_sign_in_at"
50
+ t.string "current_sign_in_ip"
51
+ t.string "last_sign_in_ip"
52
+ t.integer "failed_attempts", :default => 0
53
+ t.string "unlock_token"
54
+ t.datetime "locked_at"
55
+ t.datetime "created_at", :null => false
56
+ t.datetime "updated_at", :null => false
32
57
  end
33
58
 
34
- add_index "users", ["authy_id"], :name => "index_users_on_authy_id"
35
59
  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
36
60
  add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
37
61
 
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Authy Inc."]
14
- s.date = "2014-06-11"
14
+ s.date = "2014-12-03"
15
15
  s.description = "Authy plugin for Devise"
16
16
  s.email = "support@authy.com"
17
17
  s.extra_rdoc_files = [
@@ -56,12 +56,15 @@ Gem::Specification.new do |s|
56
56
  "authy-devise-demo/app/helpers/welcome_helper.rb",
57
57
  "authy-devise-demo/app/mailers/.gitkeep",
58
58
  "authy-devise-demo/app/models/.gitkeep",
59
+ "authy-devise-demo/app/models/admin.rb",
59
60
  "authy-devise-demo/app/models/user.rb",
60
61
  "authy-devise-demo/app/views/devise/devise_authy/enable_authy.html.erb",
61
62
  "authy-devise-demo/app/views/devise/devise_authy/verify_authy.html.erb",
62
63
  "authy-devise-demo/app/views/devise/devise_authy/verify_authy_installation.html.erb",
63
64
  "authy-devise-demo/app/views/layouts/application.html.erb",
65
+ "authy-devise-demo/app/views/welcome/admin_page.html.erb",
64
66
  "authy-devise-demo/app/views/welcome/index.html.erb",
67
+ "authy-devise-demo/app/views/welcome/user_page.html.erb",
65
68
  "authy-devise-demo/config.ru",
66
69
  "authy-devise-demo/config/application.rb",
67
70
  "authy-devise-demo/config/boot.rb",
@@ -83,7 +86,8 @@ Gem::Specification.new do |s|
83
86
  "authy-devise-demo/config/locales/en.yml",
84
87
  "authy-devise-demo/config/routes.rb",
85
88
  "authy-devise-demo/db/migrate/20130409234357_devise_create_users.rb",
86
- "authy-devise-demo/db/migrate/20130409234434_devise_authy_add_to_users.rb",
89
+ "authy-devise-demo/db/migrate/20141202000744_devise_create_admins.rb",
90
+ "authy-devise-demo/db/migrate/20141202004246_devise_authy_add_to_admins.rb",
87
91
  "authy-devise-demo/db/schema.rb",
88
92
  "authy-devise-demo/db/seeds.rb",
89
93
  "authy-devise-demo/lib/assets/.gitkeep",
@@ -116,6 +120,7 @@ Gem::Specification.new do |s|
116
120
  "lib/devise-authy/hooks/authy_authenticatable.rb",
117
121
  "lib/devise-authy/mapping.rb",
118
122
  "lib/devise-authy/models/authy_authenticatable.rb",
123
+ "lib/devise-authy/models/authy_lockable.rb",
119
124
  "lib/devise-authy/rails.rb",
120
125
  "lib/devise-authy/routes.rb",
121
126
  "lib/generators/active_record/devise_authy_generator.rb",
@@ -125,8 +130,10 @@ Gem::Specification.new do |s|
125
130
  "spec/controllers/devise_authy_controller_spec.rb",
126
131
  "spec/controllers/passwords_controller_spec.rb",
127
132
  "spec/features/authy_authenticatable_spec.rb",
133
+ "spec/features/authy_lockable_spec.rb",
128
134
  "spec/generators_spec.rb",
129
- "spec/models/authy_authenticatable.rb",
135
+ "spec/models/authy_authenticatable_spec.rb",
136
+ "spec/models/authy_lockable_spec.rb",
130
137
  "spec/orm/active_record.rb",
131
138
  "spec/rails-app/Gemfile",
132
139
  "spec/rails-app/Gemfile.lock",
@@ -144,6 +151,7 @@ Gem::Specification.new do |s|
144
151
  "spec/rails-app/app/helpers/welcome_helper.rb",
145
152
  "spec/rails-app/app/mailers/.gitkeep",
146
153
  "spec/rails-app/app/models/.gitkeep",
154
+ "spec/rails-app/app/models/lockable_user.rb",
147
155
  "spec/rails-app/app/models/user.rb",
148
156
  "spec/rails-app/app/views/devise/devise_authy/enable_authy.html.erb",
149
157
  "spec/rails-app/app/views/devise/devise_authy/verify_authy.html.erb",
@@ -189,7 +197,7 @@ Gem::Specification.new do |s|
189
197
  ]
190
198
  s.homepage = "https://github.com/authy/authy-devise"
191
199
  s.licenses = ["MIT"]
192
- s.rubygems_version = "2.2.2"
200
+ s.rubygems_version = "2.4.3"
193
201
  s.summary = "Authy plugin for Devise"
194
202
 
195
203
  if s.respond_to? :specification_version then
@@ -24,5 +24,7 @@ end
24
24
  require 'devise-authy/routes'
25
25
  require 'devise-authy/rails'
26
26
  require 'devise-authy/models/authy_authenticatable'
27
+ require 'devise-authy/models/authy_lockable'
27
28
 
28
29
  Devise.add_module :authy_authenticatable, :model => 'devise-authy/models/authy_authenticatable', :controller => :devise_authy, :route => :authy
30
+ Devise.add_module :authy_lockable, :model => 'devise-authy/models/authy_lockable'
@@ -26,10 +26,14 @@ module DeviseAuthy
26
26
  return true
27
27
  end
28
28
 
29
+ def is_devise_sessions_controller?
30
+ self.class == Devise::SessionsController || self.class.ancestors.include?(Devise::SessionsController)
31
+ end
32
+
29
33
  def is_signing_in?
30
34
  if devise_controller? && signed_in?(resource_name) &&
31
- self.class == Devise::SessionsController || self.class.ancestors.include?(Devise::SessionsController) &&
32
- self.action_name == "create"
35
+ is_devise_sessions_controller? &&
36
+ self.action_name == "create"
33
37
  return true
34
38
  end
35
39
 
@@ -65,4 +69,4 @@ module DeviseAuthy
65
69
  end
66
70
  end
67
71
  end
68
- end
72
+ end
@@ -0,0 +1,43 @@
1
+ module Devise
2
+
3
+ module Models
4
+
5
+ # Handles blocking a user access after a certain number of attempts.
6
+ # Requires proper configuration of the Devise::Models::Lockable module.
7
+ #
8
+ module AuthyLockable
9
+
10
+ extend ActiveSupport::Concern
11
+
12
+ # Public: Determine if this is a lockable resource, via Devise::Models::Lockable.
13
+ # Returns true
14
+ # Raises an error if the Devise::Models::Lockable module is not configured.
15
+ def lockable?
16
+ raise 'Devise lockable extension required' unless respond_to? :lock_access!
17
+ Devise.lock_strategy == :failed_attempts
18
+ end
19
+
20
+ # Public: Handle a failed 2FA attempt. If the resource is lockable via
21
+ # Devise::Models::Lockable module then enforce that setting.
22
+ #
23
+ # Returns true if the user is locked out.
24
+ def invalid_authy_attempt!
25
+ return false unless lockable?
26
+
27
+ self.failed_attempts ||= 0
28
+ self.failed_attempts += 1
29
+
30
+ if attempts_exceeded?
31
+ lock_access! unless access_locked?
32
+ true
33
+ else
34
+ save validate: false
35
+ false
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -62,6 +62,46 @@ describe Devise::DeviseAuthyController do
62
62
  post :POST_verify_authy, :token => '5678900'
63
63
  response.should render_template('verify_authy')
64
64
  end
65
+
66
+ context 'User is lockable' do
67
+
68
+ let(:user) { create_lockable_user authy_id: 2 }
69
+
70
+ before do
71
+ controller.stub(:find_resource).and_return user
72
+ controller.instance_variable_set :@resource, user
73
+ end
74
+
75
+ it 'locks the account when failed_attempts exceeds maximum' do
76
+ request.session['user_id'] = user.id
77
+ request.session['user_password_checked'] = true
78
+
79
+ too_many_failed_attempts.times do
80
+ post :POST_verify_authy, token: invalid_authy_token
81
+ end
82
+
83
+ user.reload
84
+ expect(user.access_locked?).to be_true
85
+ end
86
+
87
+ end
88
+
89
+ context 'User is not lockable' do
90
+
91
+ it 'does not lock the account when failed_attempts exceeds maximum' do
92
+ request.session['user_id'] = @user.id
93
+ request.session['user_password_checked'] = true
94
+
95
+ too_many_failed_attempts.times do
96
+ post :POST_verify_authy, token: invalid_authy_token
97
+ end
98
+
99
+ @user.reload
100
+ expect(@user.locked_at).to be_nil
101
+ end
102
+
103
+ end
104
+
65
105
  end
66
106
 
67
107
  describe "GET #enable_authy" do
@@ -26,7 +26,6 @@ describe "Authy Autnenticatable", :type => :request do
26
26
  end
27
27
 
28
28
  it "Sign in should succeed" do
29
- visit new_user_session_path
30
29
  fill_sign_in_form(@user.email, '12345678')
31
30
  current_path.should == user_verify_authy_path
32
31
  page.should have_content('Please enter your Authy token')
@@ -42,7 +41,6 @@ describe "Authy Autnenticatable", :type => :request do
42
41
  end
43
42
 
44
43
  it "Sign in shouldn't succeed" do
45
- visit new_user_session_path
46
44
  fill_sign_in_form(@user.email, '12345678')
47
45
  current_path.should == user_verify_authy_path
48
46
  page.should have_content('Please enter your Authy token')
@@ -60,7 +58,6 @@ describe "Authy Autnenticatable", :type => :request do
60
58
  it "Should prompt for a token" do
61
59
  cookie_val = sign_cookie("remember_device", Time.now.to_i - 2.month.to_i)
62
60
  page.driver.browser.set_cookie("remember_device=#{cookie_val}")
63
- visit new_user_session_path
64
61
  fill_sign_in_form(@user.email, '12345678')
65
62
  current_path.should == user_verify_authy_path
66
63
  page.should have_content('Please enter your Authy token')
@@ -69,7 +66,6 @@ describe "Authy Autnenticatable", :type => :request do
69
66
  it "Shouldn't prompt for a token" do
70
67
  cookie_val = sign_cookie("remember_device", Time.now.to_i)
71
68
  page.driver.browser.set_cookie("remember_device=#{cookie_val}")
72
- visit new_user_session_path
73
69
  fill_sign_in_form(@user.email, '12345678')
74
70
  current_path.should == root_path
75
71
  page.should have_content("Signed in successfully.")
@@ -86,7 +82,6 @@ describe "Authy Autnenticatable", :type => :request do
86
82
  end
87
83
 
88
84
  it "Click link Request sms" do
89
- visit new_user_session_path
90
85
  fill_sign_in_form(@user.email, '12345678')
91
86
  click_link 'Request SMS'
92
87
  page.should have_content("SMS token was sent")
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Authy Lockable' do
4
+
5
+ context 'during verify code when Authy enabled' do
6
+
7
+ let(:user) do
8
+ u = create_lockable_user authy_id: 20, email: 'foo@bar.com'
9
+ u.update_attribute :authy_enabled, true
10
+ u
11
+ end
12
+
13
+ before :each do
14
+ fill_sign_in_form user.email, '12345678', '#new_lockable_user', new_lockable_user_session_path
15
+ end
16
+
17
+ scenario 'account locked when user enters invalid code too many times' do
18
+ Devise.maximum_attempts.times do |i|
19
+ fill_verify_token_form invalid_authy_token
20
+ assert_at lockable_user_verify_authy_path
21
+ expect(page).to have_content('Please enter your Authy token')
22
+ user.reload
23
+ assert_account_locked_for user, nil
24
+ expect(user.failed_attempts).to eq(i + 1)
25
+ end
26
+
27
+ fill_verify_token_form invalid_authy_token
28
+ user.reload
29
+ assert_at new_user_session_path
30
+ assert_account_locked_for user
31
+ visit root_path
32
+ assert_at new_user_session_path
33
+ end
34
+
35
+ end
36
+
37
+ context 'during verify Authy installation' do
38
+
39
+ let(:user) { create_lockable_user email: 'foo@bar.com' }
40
+
41
+ before do
42
+ fill_sign_in_form user.email, '12345678', '#new_lockable_user', new_lockable_user_session_path
43
+ end
44
+
45
+ scenario 'account locked when user enters invalid code too many times' do
46
+ visit lockable_user_enable_authy_path
47
+ fill_in 'authy-countries', with: '1'
48
+ fill_in 'authy-cellphone', with: '8001234567'
49
+ click_on 'Enable'
50
+
51
+ Devise.maximum_attempts.times do |i|
52
+ fill_in_verify_authy_installation_form invalid_authy_token
53
+ assert_at lockable_user_verify_authy_installation_path
54
+ expect(page).to have_content('Verify your account')
55
+ user.reload
56
+ assert_account_locked_for user, nil
57
+ expect(user.failed_attempts).to eq(i + 1)
58
+ end
59
+
60
+ fill_in_verify_authy_installation_form invalid_authy_token
61
+ user.reload
62
+ assert_at new_user_session_path
63
+ assert_account_locked_for user
64
+ visit root_path
65
+ assert_at new_user_session_path
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Devise::Models::AuthyLockable do
4
+
5
+ context 'model includes Devise::Models::Lockable' do
6
+
7
+ let(:user) { create_lockable_user authy_id: '20' }
8
+
9
+ context '#lockable?' do
10
+
11
+ it 'returns true if lock_strategy is :failed_attempts' do
12
+ expect(user.lockable?).to be_true
13
+ end
14
+
15
+ it 'returns false if lock_strategy is anything other than :failed attempts' do
16
+ Devise.lock_strategy = :none
17
+ expect(user.lockable?).to be_false
18
+ Devise.lock_strategy = :failed_attempts
19
+ end
20
+
21
+ end
22
+
23
+ context '#invalid_authy_attempt!' do
24
+
25
+ it 'resets failed_attempts to 0 if nil' do
26
+ user.update_attribute :failed_attempts, nil
27
+ user.invalid_authy_attempt!
28
+ expect(user.failed_attempts).to eq(1)
29
+ end
30
+
31
+ it 'updates failed_attempts' do
32
+ 10.times { user.invalid_authy_attempt! }
33
+ expect(user.failed_attempts).to eq(10)
34
+ end
35
+
36
+ it 'respects the maximum attempts configuration for Devise::Models::Lockable' do
37
+ 4.times { user.invalid_authy_attempt! }
38
+ expect(user.send :attempts_exceeded?).to be_true # protected method
39
+ expect(user.access_locked?).to be_true
40
+ end
41
+
42
+ it 'returns true if the account is locked' do
43
+ 3.times { user.invalid_authy_attempt! }
44
+ expect(user.invalid_authy_attempt!).to be_true
45
+ end
46
+
47
+ it 'returns false if the account is not locked' do
48
+ expect(user.invalid_authy_attempt!).to be_false
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ context 'model misconfigured, includes AuthyLockable w/out Lockable' do
56
+
57
+ let(:user) do
58
+ u = create_user authy_id: '20'
59
+ u.extend Devise::Models::AuthyLockable
60
+ u
61
+ end
62
+
63
+ context '#lockable?' do
64
+
65
+ it 'raises an error' do
66
+ expect { user.lockable? }.to raise_error 'Devise lockable extension required'
67
+ end
68
+
69
+ end
70
+
71
+ context '#invalid_authy_attempt!' do
72
+
73
+ it 'raises an error' do
74
+ expect { user.invalid_authy_attempt! }.to raise_error 'Devise lockable extension required'
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -1,6 +1,13 @@
1
1
  class WelcomeController < ApplicationController
2
- before_filter :authenticate_user!
2
+ before_filter :authenticate_scope!
3
3
 
4
4
  def index
5
5
  end
6
+
7
+ def authenticate_scope!
8
+ scope = lockable_user_signed_in? ? 'lockable_user'
9
+ : 'user'
10
+ Rails.logger.debug "\nauthenticate_#{scope}!"
11
+ send "authenticate_#{scope}!", force: true
12
+ end
6
13
  end
@@ -0,0 +1,7 @@
1
+ # User for testing out the account locking on failure.
2
+ #
3
+ class LockableUser < User
4
+ devise :authy_authenticatable, :authy_lockable, :database_authenticatable,
5
+ :registerable, :recoverable, :rememberable, :trackable, :validatable,
6
+ :lockable
7
+ end
@@ -141,7 +141,7 @@ Devise.setup do |config|
141
141
  # Defines which strategy will be used to lock an account.
142
142
  # :failed_attempts = Locks an account after a number of failed attempts to sign in.
143
143
  # :none = No lock strategy. You should handle locking by yourself.
144
- # config.lock_strategy = :failed_attempts
144
+ config.lock_strategy = :failed_attempts
145
145
 
146
146
  # Defines which key will be used when locking and unlocking an account
147
147
  # config.unlock_keys = [ :email ]
@@ -151,14 +151,14 @@ Devise.setup do |config|
151
151
  # :time = Re-enables login after a certain amount of time (see :unlock_in below)
152
152
  # :both = Enables both strategies
153
153
  # :none = No unlock strategy. You should handle unlocking by yourself.
154
- # config.unlock_strategy = :both
154
+ config.unlock_strategy = :time
155
155
 
156
156
  # Number of authentication tries before locking an account if lock_strategy
157
157
  # is failed attempts.
158
- # config.maximum_attempts = 20
158
+ config.maximum_attempts = 3
159
159
 
160
160
  # Time interval to unlock the account if :time is enabled as unlock_strategy.
161
- # config.unlock_in = 1.hour
161
+ config.unlock_in = 1.hour
162
162
 
163
163
  # ==> Configuration for :recoverable
164
164
  #
@@ -2,6 +2,7 @@ RailsApp::Application.routes.draw do
2
2
  get "welcome/index"
3
3
 
4
4
  devise_for :users
5
+ devise_for :lockable_users, class: 'LockableUser' # for testing authy_lockable
5
6
 
6
7
  root :to => 'welcome#index'
7
8
  end
@@ -26,9 +26,9 @@ class DeviseCreateUsers < ActiveRecord::Migration
26
26
  # t.string :unconfirmed_email # Only if using reconfirmable
27
27
 
28
28
  ## Lockable
29
- # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30
- # t.string :unlock_token # Only if unlock strategy is :email or :both
31
- # t.datetime :locked_at
29
+ t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30
+ t.string :unlock_token # Only if unlock strategy is :email or :both
31
+ t.datetime :locked_at
32
32
 
33
33
  ## Token authenticatable
34
34
  # t.string :authentication_token
@@ -24,6 +24,9 @@ ActiveRecord::Schema.define(:version => 20130419164936) do
24
24
  t.datetime "last_sign_in_at"
25
25
  t.string "current_sign_in_ip"
26
26
  t.string "last_sign_in_ip"
27
+ t.integer "failed_attempts", :default => 0
28
+ t.string "unlock_token"
29
+ t.datetime "locked_at"
27
30
  t.datetime "created_at", :null => false
28
31
  t.datetime "updated_at", :null => false
29
32
  t.string "authy_id"
@@ -18,16 +18,61 @@ def create_user(attributes={})
18
18
  User.create!(valid_attributes(attributes))
19
19
  end
20
20
 
21
- def fill_sign_in_form(email, password)
22
- visit new_user_session_path
23
- within("#new_user") do
21
+ def create_lockable_user(attributes={})
22
+ LockableUser.create!(valid_attributes(attributes))
23
+ end
24
+
25
+ def fill_sign_in_form(email, password, form_selector = nil, sign_in_path = nil)
26
+ form_selector ||= '#new_user'
27
+ sign_in_path ||= new_user_session_path
28
+
29
+ visit sign_in_path
30
+ within(form_selector) do
24
31
  fill_in 'Email', :with => email
25
32
  fill_in 'Password', :with => password
26
33
  end
27
34
  click_on 'Sign in'
28
35
  end
29
36
 
37
+ def fill_verify_token_form(token)
38
+ within('#devise_authy') { fill_in 'authy-token', with: token }
39
+ click_on 'Check Token'
40
+ end
41
+
42
+ def fill_in_verify_authy_installation_form(token)
43
+ fill_in 'authy-token', with: token
44
+ click_on 'Enable my account'
45
+ end
46
+
30
47
  def sign_cookie(name, val)
31
48
  verifier = ActiveSupport::MessageVerifier.new(RailsApp::Application.config.secret_token)
32
49
  verifier.generate(val)
33
50
  end
51
+
52
+ def too_many_failed_attempts
53
+ Devise.maximum_attempts + 1
54
+ end
55
+
56
+ def valid_authy_token
57
+ '0000000'
58
+ end
59
+
60
+ def invalid_authy_token
61
+ '999999'
62
+ end
63
+
64
+ def lock_user_account
65
+ too_many_failed_attempts.times { fill_verify_token_form invalid_authy_token }
66
+ end
67
+
68
+ def assert_at(path)
69
+ expect(current_path).to eq(path)
70
+ end
71
+
72
+ def assert_not_at(path)
73
+ expect(current_path).not_to eq(path)
74
+ end
75
+
76
+ def assert_account_locked_for(user, is_locked = true)
77
+ expect(user.access_locked?).to eq(is_locked)
78
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-authy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.3
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Authy Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-11 00:00:00.000000000 Z
11
+ date: 2015-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -209,12 +209,15 @@ files:
209
209
  - authy-devise-demo/app/helpers/welcome_helper.rb
210
210
  - authy-devise-demo/app/mailers/.gitkeep
211
211
  - authy-devise-demo/app/models/.gitkeep
212
+ - authy-devise-demo/app/models/admin.rb
212
213
  - authy-devise-demo/app/models/user.rb
213
214
  - authy-devise-demo/app/views/devise/devise_authy/enable_authy.html.erb
214
215
  - authy-devise-demo/app/views/devise/devise_authy/verify_authy.html.erb
215
216
  - authy-devise-demo/app/views/devise/devise_authy/verify_authy_installation.html.erb
216
217
  - authy-devise-demo/app/views/layouts/application.html.erb
218
+ - authy-devise-demo/app/views/welcome/admin_page.html.erb
217
219
  - authy-devise-demo/app/views/welcome/index.html.erb
220
+ - authy-devise-demo/app/views/welcome/user_page.html.erb
218
221
  - authy-devise-demo/config.ru
219
222
  - authy-devise-demo/config/application.rb
220
223
  - authy-devise-demo/config/boot.rb
@@ -236,7 +239,8 @@ files:
236
239
  - authy-devise-demo/config/locales/en.yml
237
240
  - authy-devise-demo/config/routes.rb
238
241
  - authy-devise-demo/db/migrate/20130409234357_devise_create_users.rb
239
- - authy-devise-demo/db/migrate/20130409234434_devise_authy_add_to_users.rb
242
+ - authy-devise-demo/db/migrate/20141202000744_devise_create_admins.rb
243
+ - authy-devise-demo/db/migrate/20141202004246_devise_authy_add_to_admins.rb
240
244
  - authy-devise-demo/db/schema.rb
241
245
  - authy-devise-demo/db/seeds.rb
242
246
  - authy-devise-demo/lib/assets/.gitkeep
@@ -269,6 +273,7 @@ files:
269
273
  - lib/devise-authy/hooks/authy_authenticatable.rb
270
274
  - lib/devise-authy/mapping.rb
271
275
  - lib/devise-authy/models/authy_authenticatable.rb
276
+ - lib/devise-authy/models/authy_lockable.rb
272
277
  - lib/devise-authy/rails.rb
273
278
  - lib/devise-authy/routes.rb
274
279
  - lib/generators/active_record/devise_authy_generator.rb
@@ -278,8 +283,10 @@ files:
278
283
  - spec/controllers/devise_authy_controller_spec.rb
279
284
  - spec/controllers/passwords_controller_spec.rb
280
285
  - spec/features/authy_authenticatable_spec.rb
286
+ - spec/features/authy_lockable_spec.rb
281
287
  - spec/generators_spec.rb
282
- - spec/models/authy_authenticatable.rb
288
+ - spec/models/authy_authenticatable_spec.rb
289
+ - spec/models/authy_lockable_spec.rb
283
290
  - spec/orm/active_record.rb
284
291
  - spec/rails-app/Gemfile
285
292
  - spec/rails-app/Gemfile.lock
@@ -297,6 +304,7 @@ files:
297
304
  - spec/rails-app/app/helpers/welcome_helper.rb
298
305
  - spec/rails-app/app/mailers/.gitkeep
299
306
  - spec/rails-app/app/models/.gitkeep
307
+ - spec/rails-app/app/models/lockable_user.rb
300
308
  - spec/rails-app/app/models/user.rb
301
309
  - spec/rails-app/app/views/devise/devise_authy/enable_authy.html.erb
302
310
  - spec/rails-app/app/views/devise/devise_authy/verify_authy.html.erb
@@ -1,18 +0,0 @@
1
- class DeviseAuthyAddToUsers < ActiveRecord::Migration
2
- def self.up
3
- change_table :users do |t|
4
- t.string :authy_id
5
- t.datetime :last_sign_in_with_authy
6
- t.boolean :authy_enabled, :default => false
7
- end
8
-
9
- add_index :users, :authy_id
10
- end
11
-
12
- def self.down
13
- change_table :users do |t|
14
- t.remove :authy_id, :last_sign_in_with_authy, :authy_enabled
15
- end
16
- end
17
- end
18
-