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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +22 -1
- data/VERSION +1 -1
- data/app/controllers/devise/devise_authy_controller.rb +25 -12
- data/app/controllers/devise_authy/passwords_controller.rb +2 -2
- data/authy-devise-demo/Gemfile.lock +4 -4
- data/authy-devise-demo/app/controllers/welcome_controller.rb +10 -1
- data/authy-devise-demo/app/models/admin.rb +10 -0
- data/authy-devise-demo/app/models/user.rb +3 -4
- data/authy-devise-demo/app/views/welcome/admin_page.html.erb +12 -0
- data/authy-devise-demo/app/views/welcome/index.html.erb +2 -7
- data/authy-devise-demo/app/views/welcome/user_page.html.erb +5 -0
- data/authy-devise-demo/config/initializers/devise.rb +3 -3
- data/authy-devise-demo/config/locales/devise.authy.en.yml +2 -2
- data/authy-devise-demo/config/routes.rb +4 -1
- data/authy-devise-demo/db/migrate/20130409234357_devise_create_users.rb +3 -3
- data/authy-devise-demo/db/migrate/20141202000744_devise_create_admins.rb +46 -0
- data/authy-devise-demo/db/migrate/20141202004246_devise_authy_add_to_admins.rb +21 -0
- data/authy-devise-demo/db/schema.rb +27 -3
- data/devise-authy.gemspec +12 -4
- data/lib/devise-authy.rb +2 -0
- data/lib/devise-authy/controllers/helpers.rb +7 -3
- data/lib/devise-authy/models/authy_lockable.rb +43 -0
- data/spec/controllers/devise_authy_controller_spec.rb +40 -0
- data/spec/features/authy_authenticatable_spec.rb +0 -5
- data/spec/features/authy_lockable_spec.rb +70 -0
- data/spec/models/{authy_authenticatable.rb → authy_authenticatable_spec.rb} +0 -0
- data/spec/models/authy_lockable_spec.rb +81 -0
- data/spec/rails-app/app/controllers/welcome_controller.rb +8 -1
- data/spec/rails-app/app/models/lockable_user.rb +7 -0
- data/spec/rails-app/config/initializers/devise.rb +4 -4
- data/spec/rails-app/config/routes.rb +1 -0
- data/spec/rails-app/db/development.sqlite3 +0 -0
- data/spec/rails-app/db/migrate/20130419164907_devise_create_users.rb +3 -3
- data/spec/rails-app/db/schema.rb +3 -0
- data/spec/support/helpers.rb +48 -3
- metadata +12 -4
- 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:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e459c469815b8231e8134201d93fce4549dae9a6
         | 
| 4 | 
            +
              data.tar.gz: a049bec15607162733009e40118b738596f476ae
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ac16e44154437ba153eba18c70960b49f49f80de71f11e9fb3784d2b4ec2a0e5a55661379d92fa72722566966e8337073b27341384db9c3d3c14a198772802d7
         | 
| 7 | 
            +
              data.tar.gz: c826d6ef69433cff85a45d79a0267c922d5f7ee8e59233347ecaa5d4758dd9d32b2b07152d945e9912e2d8910a41985ceb01011c7d4c9180c5e79dea1fd096ed
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -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. | 
| 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. | 
| 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/ | 
| 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. | 
| 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 | 
            -
                   | 
| 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 | 
            -
                   | 
| 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 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 150 | 
            +
              def after_authy_enabled_path_for(resource)
         | 
| 151 | 
            +
                root_path
         | 
| 152 | 
            +
              end
         | 
| 153 153 |  | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 154 | 
            +
              def after_authy_verified_path_for(resource)
         | 
| 155 | 
            +
                after_authy_enabled_path_for(resource)
         | 
| 156 | 
            +
              end
         | 
| 157 157 |  | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 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. | 
| 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. | 
| 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. | 
| 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. | 
| 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 : | 
| 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 : | 
| 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 | 
            -
             | 
| 5 | 
            -
             | 
| 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 %>
         | 
| @@ -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 | 
            -
               | 
| 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 | 
            -
               | 
| 150 | 
            +
              config.maximum_attempts = 20
         | 
| 151 151 |  | 
| 152 152 | 
             
              # Time interval to unlock the account if :time is enabled as unlock_strategy.
         | 
| 153 | 
            -
               | 
| 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 | 
            -
                   | 
| 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 | 
            -
             | 
| 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 | 
            -
                   | 
| 30 | 
            -
                   | 
| 31 | 
            -
                   | 
| 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 =>  | 
| 14 | 
            +
            ActiveRecord::Schema.define(:version => 20141202004246) do
         | 
| 15 15 |  | 
| 16 | 
            -
              create_table " | 
| 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 |  | 
    
        data/devise-authy.gemspec
    CHANGED
    
    | @@ -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- | 
| 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/ | 
| 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/ | 
| 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. | 
| 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
         | 
    
        data/lib/devise-authy.rb
    CHANGED
    
    | @@ -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 | 
            -
             | 
| 32 | 
            -
             | 
| 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
         | 
| 
            File without changes
         | 
| @@ -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 : | 
| 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
         | 
| @@ -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 | 
            -
               | 
| 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 | 
            -
               | 
| 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 | 
            -
               | 
| 158 | 
            +
              config.maximum_attempts = 3
         | 
| 159 159 |  | 
| 160 160 | 
             
              # Time interval to unlock the account if :time is enabled as unlock_strategy.
         | 
| 161 | 
            -
               | 
| 161 | 
            +
              config.unlock_in = 1.hour
         | 
| 162 162 |  | 
| 163 163 | 
             
              # ==> Configuration for :recoverable
         | 
| 164 164 | 
             
              #
         | 
| Binary file | 
| @@ -26,9 +26,9 @@ class DeviseCreateUsers < ActiveRecord::Migration | |
| 26 26 | 
             
                  # t.string   :unconfirmed_email # Only if using reconfirmable
         | 
| 27 27 |  | 
| 28 28 | 
             
                  ## Lockable
         | 
| 29 | 
            -
                   | 
| 30 | 
            -
                   | 
| 31 | 
            -
                   | 
| 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
         | 
    
        data/spec/rails-app/db/schema.rb
    CHANGED
    
    | @@ -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"
         | 
    
        data/spec/support/helpers.rb
    CHANGED
    
    | @@ -18,16 +18,61 @@ def create_user(attributes={}) | |
| 18 18 | 
             
              User.create!(valid_attributes(attributes))
         | 
| 19 19 | 
             
            end
         | 
| 20 20 |  | 
| 21 | 
            -
            def  | 
| 22 | 
            -
               | 
| 23 | 
            -
             | 
| 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. | 
| 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:  | 
| 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/ | 
| 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/ | 
| 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 | 
            -
             |