devise-authy 1.5.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-