two_factor_authentication 1.1.1 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8037c367e1a0f7f7d253be0457b2046488b628fa
4
- data.tar.gz: 024269293da1e0a407ac4e3537dc4e8749ca95a5
3
+ metadata.gz: fb84a503671c99963674feb9a48b9e0b4078c457
4
+ data.tar.gz: e1004e05b1adfb4fc9dbae58638f3091a29cd4c9
5
5
  SHA512:
6
- metadata.gz: 0c5a7deda98812e0a46ced0f99f2c0af64d4cd57531aad76660fecc7a1b351ba776263b24066a3d1f8caa93279f97fba1d415bcf41caf13bb1bc627a3a0c49ba
7
- data.tar.gz: 2c80f6553ff57dbc3257f20c345cdf2217eefcf7d543f7547c0c31f8e57dbfc94ccf27759acbf81ba3937b05a808a8cc42acbb3ce7ee1ee7cb4869379f02bbaf
6
+ metadata.gz: 7f790fd330b74c0bed8199477a23dce4d52d5035c86495a62d2d5e5b0bc77f5d97c4cea8b6b9aee5a6b3b306d9d45664fb95b5a0c3ca89838b3a832302de1590
7
+ data.tar.gz: 48d79bca0d7eb2f51ed606d4c34aea8b7f7d01cf0c2e59e763a0fd6eaad167dbc53c4c7db495bc0931fa6b061c4a095a6d521b868334eeaf77267538861b3cf6
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ rails = case rails_version
9
9
  when "master"
10
10
  {github: "rails/rails"}
11
11
  when "default"
12
- "~> 3.2"
12
+ "~> 4.1"
13
13
  else
14
14
  "~> #{rails_version}"
15
15
  end
data/README.md CHANGED
@@ -38,11 +38,12 @@ Add the following line to your model to fully enable two-factor auth:
38
38
 
39
39
  has_one_time_password
40
40
 
41
- Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
41
+ Set config values, if desired, for maximum second factor attempts count, allowed time drift, and OTP length.
42
42
 
43
43
  ```ruby
44
44
  config.max_login_attempts = 3
45
45
  config.allowed_otp_drift_seconds = 30
46
+ config.otp_length = 6
46
47
  ```
47
48
 
48
49
  Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
@@ -66,11 +67,12 @@ Add the following line to your model to fully enable two-factor auth:
66
67
 
67
68
  has_one_time_password
68
69
 
69
- Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
70
+ Set config values, if desired, for maximum second factor attempts count, allowed time drift, and OTP length.
70
71
 
71
72
  ```ruby
72
73
  config.max_login_attempts = 3
73
74
  config.allowed_otp_drift_seconds = 30
75
+ config.otp_length = 6
74
76
  ```
75
77
 
76
78
  Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
@@ -81,6 +83,7 @@ def send_two_factor_authentication_code
81
83
  end
82
84
  ```
83
85
 
86
+
84
87
  ### Customisation and Usage
85
88
 
86
89
  By default second factor authentication enabled for each user, you can change it with this method in your User model:
@@ -99,6 +102,42 @@ This gem is compatible with Google Authenticator (https://support.google.com/acc
99
102
 
100
103
  This provisioning uri can then be turned in to a QR code if desired so that users may add the app to Google Authenticator easily. Once this is done they may retrieve a one-time password directly from the Google Authenticator app as well as through whatever method you define in `send_two_factor_authentication_code`
101
104
 
105
+ #### Overriding the view
106
+
107
+ The default view that shows the form can be overridden by first adding a folder named: "two_factor_authentication" inside "app/views/devise", in here you want to create a "show.html.erb" view.
108
+
109
+ The full path should be "app/views/devise/two_factor_authentication/show.html.erb"
110
+
111
+ ```html
112
+ <h2>Hi, you received a code by email, please enter it below, thanks!</h2>
113
+
114
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
115
+ <%= text_field_tag :code %>
116
+ <%= submit_tag "Log in!" %>
117
+ <% end %>
118
+
119
+ <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
120
+
121
+ ```
122
+
123
+ #### Updating existing users with OTP secret key
124
+
125
+ If you have existing users that needs to be provided with a OTP secret key, so they can take benefit of the two factor authentication, create a rake. It could look like this one below:
126
+
127
+ ```ruby
128
+ desc "rake task to update users with otp secret key"
129
+ task :update_users_with_otp_secret_key => :environment do
130
+ users = User.all
131
+
132
+ users.each do |user|
133
+ key = ROTP::Base32.random_base32
134
+ user.update_attributes(:otp_secret_key => key)
135
+ user.save
136
+ puts "Rake[:update_users_with_otp_secret_key] => User '#{user.email}' OTP secret key set to '#{key}'"
137
+ end
138
+ end
139
+ ```
140
+
102
141
  ### Example
103
142
 
104
143
  [TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
@@ -9,7 +9,7 @@ class Devise::TwoFactorAuthenticationController < DeviseController
9
9
  render :show and return if params[:code].nil?
10
10
 
11
11
  if resource.authenticate_otp(params[:code])
12
- warden.session(resource_name)['need_two_factor_authentication'] = false
12
+ warden.session(resource_name)[TwoFactorAuthentication::NEED_AUTHENTICATION] = false
13
13
  sign_in resource_name, resource, :bypass => true
14
14
  set_flash_message :notice, :success
15
15
  redirect_to stored_location_for(resource_name) || :root
@@ -1,6 +1,7 @@
1
1
  ru:
2
2
  devise:
3
3
  two_factor_authentication:
4
+ success: "Двухфакторная авторизация успешно пройдена."
4
5
  attempt_failed: "Неверный код."
5
6
  max_login_attempts_reached: "Доступ заблокирован. Превышено число попыток авторизации"
6
7
  contact_administrator: "Пожалуйста, свяжитесь с системным администратором."
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
8
  def copy_two_factor_authentication_migration
9
- migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}"
9
+ migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}.rb"
10
10
  end
11
11
 
12
12
  end
@@ -13,9 +13,14 @@ module Devise
13
13
 
14
14
  mattr_accessor :allowed_otp_drift_seconds
15
15
  @@allowed_otp_drift_seconds = 30
16
+
17
+ mattr_accessor :otp_length
18
+ @@otp_length = 6
16
19
  end
17
20
 
18
21
  module TwoFactorAuthentication
22
+ NEED_AUTHENTICATION = 'need_two_factor_authentication'
23
+
19
24
  autoload :Schema, 'two_factor_authentication/schema'
20
25
  module Controllers
21
26
  autoload :Helpers, 'two_factor_authentication/controllers/helpers'
@@ -12,7 +12,7 @@ module TwoFactorAuthentication
12
12
  def handle_two_factor_authentication
13
13
  unless devise_controller?
14
14
  Devise.mappings.keys.flatten.any? do |scope|
15
- if signed_in?(scope) and warden.session(scope)['need_two_factor_authentication']
15
+ if signed_in?(scope) and warden.session(scope)[TwoFactorAuthentication::NEED_AUTHENTICATION]
16
16
  handle_failed_second_factor(scope)
17
17
  end
18
18
  end
@@ -21,7 +21,7 @@ module TwoFactorAuthentication
21
21
 
22
22
  def handle_failed_second_factor(scope)
23
23
  if request.format.present? and request.format.html?
24
- session["#{scope}_return_to"] = request.path if request.get?
24
+ session["#{scope}_return_to"] = "#{request.path}?#{request.query_string}" if request.get?
25
25
  redirect_to two_factor_authentication_path_for(scope)
26
26
  else
27
27
  render nothing: true, status: :unauthorized
@@ -42,7 +42,7 @@ module Devise
42
42
  module Controllers
43
43
  module Helpers
44
44
  def is_fully_authenticated?
45
- !session["warden.user.user.session"].try(:[], 'need_two_factor_authentication')
45
+ !session["warden.user.user.session"].try(:[], TwoFactorAuthentication::NEED_AUTHENTICATION)
46
46
  end
47
47
  end
48
48
  end
@@ -1,6 +1,6 @@
1
1
  Warden::Manager.after_authentication do |user, auth, options|
2
2
  if user.respond_to?(:need_two_factor_authentication?)
3
- if auth.session(options[:scope])['need_two_factor_authentication'] = user.need_two_factor_authentication?(auth.request)
3
+ if auth.session(options[:scope])[TwoFactorAuthentication::NEED_AUTHENTICATION] = user.need_two_factor_authentication?(auth.request)
4
4
  user.send_two_factor_authentication_code
5
5
  end
6
6
  end
@@ -20,19 +20,19 @@ module Devise
20
20
  end
21
21
  end
22
22
  end
23
- ::Devise::Models.config(self, :max_login_attempts, :allowed_otp_drift_seconds)
23
+ ::Devise::Models.config(self, :max_login_attempts, :allowed_otp_drift_seconds, :otp_length)
24
24
  end
25
25
 
26
26
  module InstanceMethodsOnActivation
27
27
  def authenticate_otp(code, options = {})
28
- totp = ROTP::TOTP.new(self.otp_column)
28
+ totp = ROTP::TOTP.new(self.otp_column, { digits: options[:otp_length] || self.class.otp_length })
29
29
  drift = options[:drift] || self.class.allowed_otp_drift_seconds
30
30
 
31
31
  totp.verify_with_drift(code, drift)
32
32
  end
33
33
 
34
- def otp_code(time = Time.now)
35
- ROTP::TOTP.new(self.otp_column).at(time, true)
34
+ def otp_code(time = Time.now, options = {})
35
+ ROTP::TOTP.new(self.otp_column, { digits: options[:otp_length] || self.class.otp_length }).at(time, true)
36
36
  end
37
37
 
38
38
  def provisioning_uri(account = nil, options = {})
@@ -1,3 +1,3 @@
1
1
  module TwoFactorAuthentication
2
- VERSION = "1.1.1".freeze
2
+ VERSION = "1.1.3".freeze
3
3
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  include Warden::Test::Helpers
4
4
 
5
- describe HomeController do
5
+ describe HomeController, :type => :controller do
6
6
  context "passed only 1st factor auth" do
7
7
  let(:user) { create_user }
8
8
 
@@ -11,10 +11,8 @@ describe HomeController do
11
11
  login_as user, scope: :user
12
12
  visit user_two_factor_authentication_path
13
13
 
14
-
15
- controller.is_fully_authenticated?.should be_true
14
+ expect(controller.is_fully_authenticated?).to be_truthy
16
15
  end
17
16
  end
18
-
19
17
  end
20
- end
18
+ end
@@ -11,14 +11,14 @@ feature "User of two factor authentication" do
11
11
  end
12
12
 
13
13
  scenario "sends two factor authentication code after sign in" do
14
- SMSProvider.messages.should be_empty
14
+ expect(SMSProvider.messages).to be_empty
15
15
 
16
16
  visit new_user_session_path
17
17
  complete_sign_in_form_for(user)
18
18
 
19
19
  expect(page).to have_content "Enter your personal code"
20
20
 
21
- SMSProvider.messages.size.should eq(1)
21
+ expect(SMSProvider.messages.size).to eq(1)
22
22
  message = SMSProvider.last_message
23
23
  expect(message.to).to eq(user.phone_number)
24
24
  expect(message.body).to eq(user.otp_code)
@@ -45,7 +45,7 @@ feature "User of two factor authentication" do
45
45
  end
46
46
 
47
47
  scenario "is redirected to TFA when path requires authentication" do
48
- visit dashboard_path
48
+ visit dashboard_path + "?A=param%20a&B=param%20b"
49
49
 
50
50
  expect(page).to_not have_content("Your Personal Dashboard")
51
51
 
@@ -54,6 +54,8 @@ feature "User of two factor authentication" do
54
54
 
55
55
  expect(page).to have_content("Your Personal Dashboard")
56
56
  expect(page).to have_content("You are signed in as Marissa")
57
+ expect(page).to have_content("Param A is param a")
58
+ expect(page).to have_content("Param B is param b")
57
59
  end
58
60
 
59
61
  scenario "is locked out after max failed attempts" do
@@ -21,11 +21,15 @@ describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
21
21
  subject
22
22
  end
23
23
 
24
+ it "should be configured length" do
25
+ expect(subject.length).to eq(Devise.otp_length)
26
+ end
27
+
24
28
  context "with a known time" do
25
29
  let(:time) { 1392852756 }
26
30
 
27
31
  it "should return a known result" do
28
- expect(subject).to eq('562202')
32
+ expect(subject).to eq("0000000524562202".split(//).last(Devise.otp_length).join)
29
33
  end
30
34
  end
31
35
 
@@ -33,7 +37,7 @@ describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
33
37
  let(:time) { 1393065856 }
34
38
 
35
39
  it "should return a known result padded with zeroes" do
36
- expect(subject).to eq('007672')
40
+ expect(subject).to eq("0000001608007672".split(//).last(Devise.otp_length).join)
37
41
  end
38
42
  end
39
43
  end
@@ -146,19 +150,19 @@ describe Devise::Models::TwoFactorAuthenticatable, '#max_login_attempts' do
146
150
 
147
151
  it "returns false as boolean" do
148
152
  instance.second_factor_attempts_count = nil
149
- expect(instance.max_login_attempts?).to be_false
153
+ expect(instance.max_login_attempts?).to be_falsey
150
154
  instance.second_factor_attempts_count = 0
151
- expect(instance.max_login_attempts?).to be_false
155
+ expect(instance.max_login_attempts?).to be_falsey
152
156
  instance.second_factor_attempts_count = 1
153
- expect(instance.max_login_attempts?).to be_false
157
+ expect(instance.max_login_attempts?).to be_falsey
154
158
  instance.second_factor_attempts_count = 2
155
- expect(instance.max_login_attempts?).to be_false
159
+ expect(instance.max_login_attempts?).to be_falsey
156
160
  end
157
161
 
158
162
  it "returns true as boolean after too many attempts" do
159
163
  instance.second_factor_attempts_count = 3
160
- expect(instance.max_login_attempts?).to be_true
164
+ expect(instance.max_login_attempts?).to be_truthy
161
165
  instance.second_factor_attempts_count = 4
162
- expect(instance.max_login_attempts?).to be_true
166
+ expect(instance.max_login_attempts?).to be_truthy
163
167
  end
164
168
  end
@@ -4,4 +4,8 @@
4
4
 
5
5
  <p>Your registered email address is <%= current_user.email %></p>
6
6
 
7
+ <p> Param A is <%= params[:A] %></p>
8
+
9
+ <p> Param B is <%= params[:B] %></p>
10
+
7
11
  <p>You can only see this page after successfully completing two factor authentication</p>
@@ -5,12 +5,13 @@ require 'rspec/rails'
5
5
 
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
  RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
8
  config.run_all_when_everything_filtered = true
10
9
  config.filter_run :focus
11
10
 
12
11
  config.use_transactional_examples = true
13
12
 
13
+ config.include Capybara::DSL
14
+
14
15
  # Run specs in random order to surface order dependencies. If you find an
15
16
  # order dependency and want to debug it, you can fix the order by providing
16
17
  # the seed, which is printed after each run.
@@ -8,7 +8,7 @@ module FeaturesSpecHelper
8
8
  def complete_sign_in_form_for(user)
9
9
  fill_in "Email", with: user.email
10
10
  fill_in "Password", with: 'password'
11
- click_button "Sign in"
11
+ find('.actions input').click # 'Sign in' or 'Log in'
12
12
  end
13
13
  end
14
14
 
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
 
32
32
  s.add_development_dependency 'bundler'
33
33
  s.add_development_dependency 'rake'
34
- s.add_development_dependency 'rspec-rails'
35
- s.add_development_dependency 'capybara'
34
+ s.add_development_dependency 'rspec-rails', '>= 3.0.1'
35
+ s.add_development_dependency 'capybara', '2.4.1'
36
36
  s.add_development_dependency 'pry'
37
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: two_factor_authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitrii Golub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-31 00:00:00.000000000 Z
11
+ date: 2014-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -100,28 +100,28 @@ dependencies:
100
100
  requirements:
101
101
  - - '>='
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 3.0.1
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '>='
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 3.0.1
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: capybara
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '>='
115
+ - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: 2.4.1
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: 2.4.1
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: pry
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -248,58 +248,5 @@ rubygems_version: 2.2.2
248
248
  signing_key:
249
249
  specification_version: 4
250
250
  summary: Two factor authentication plugin for devise
251
- test_files:
252
- - spec/controllers/two_factor_auth_spec.rb
253
- - spec/features/two_factor_authenticatable_spec.rb
254
- - spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb
255
- - spec/rails_app/.gitignore
256
- - spec/rails_app/README.md
257
- - spec/rails_app/Rakefile
258
- - spec/rails_app/app/assets/javascripts/application.js
259
- - spec/rails_app/app/assets/stylesheets/application.css
260
- - spec/rails_app/app/controllers/application_controller.rb
261
- - spec/rails_app/app/controllers/home_controller.rb
262
- - spec/rails_app/app/helpers/application_helper.rb
263
- - spec/rails_app/app/mailers/.gitkeep
264
- - spec/rails_app/app/models/.gitkeep
265
- - spec/rails_app/app/models/guest_user.rb
266
- - spec/rails_app/app/models/user.rb
267
- - spec/rails_app/app/views/home/dashboard.html.erb
268
- - spec/rails_app/app/views/home/index.html.erb
269
- - spec/rails_app/app/views/layouts/application.html.erb
270
- - spec/rails_app/config.ru
271
- - spec/rails_app/config/application.rb
272
- - spec/rails_app/config/boot.rb
273
- - spec/rails_app/config/database.yml
274
- - spec/rails_app/config/environment.rb
275
- - spec/rails_app/config/environments/development.rb
276
- - spec/rails_app/config/environments/production.rb
277
- - spec/rails_app/config/environments/test.rb
278
- - spec/rails_app/config/initializers/backtrace_silencers.rb
279
- - spec/rails_app/config/initializers/cookies_serializer.rb
280
- - spec/rails_app/config/initializers/devise.rb
281
- - spec/rails_app/config/initializers/inflections.rb
282
- - spec/rails_app/config/initializers/mime_types.rb
283
- - spec/rails_app/config/initializers/secret_token.rb
284
- - spec/rails_app/config/initializers/session_store.rb
285
- - spec/rails_app/config/initializers/wrap_parameters.rb
286
- - spec/rails_app/config/locales/devise.en.yml
287
- - spec/rails_app/config/locales/en.yml
288
- - spec/rails_app/config/routes.rb
289
- - spec/rails_app/db/migrate/20140403184646_devise_create_users.rb
290
- - spec/rails_app/db/migrate/20140407172619_two_factor_authentication_add_to_users.rb
291
- - spec/rails_app/db/migrate/20140407215513_add_nickanme_to_users.rb
292
- - spec/rails_app/db/schema.rb
293
- - spec/rails_app/lib/assets/.gitkeep
294
- - spec/rails_app/lib/sms_provider.rb
295
- - spec/rails_app/public/404.html
296
- - spec/rails_app/public/422.html
297
- - spec/rails_app/public/500.html
298
- - spec/rails_app/public/favicon.ico
299
- - spec/rails_app/script/rails
300
- - spec/spec_helper.rb
301
- - spec/support/authenticated_model_helper.rb
302
- - spec/support/capybara.rb
303
- - spec/support/features_spec_helper.rb
304
- - spec/support/sms_provider.rb
251
+ test_files: []
305
252
  has_rdoc: