devise-2fa 0.2.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a7255cc70209ac3ac13a39250a8c2e815208fd0250faa4ad6ae5374ffbac6f9
4
- data.tar.gz: 2e3f41153f267a42156741a7bc6aa118055b71b809cdd0f991ff26840cfeef85
3
+ metadata.gz: a34165f30cdc2d197acd726713f2ca7a6ed4ac2d4410aea31c10d4c12958e45d
4
+ data.tar.gz: ce5559076cac73dedcb0454982e8cd0f6dcd6d79ef2113b8e2426cc626e2cf27
5
5
  SHA512:
6
- metadata.gz: 724f253be1752edb042bddc6e1747d072ec1f6a91e7bbd337d5ee409780cb9e65bcf88d4f185d1445ecc563f2f08c618530e458e3d9a0a7dbe7530e40cbd8639
7
- data.tar.gz: dd3dc80d4c369e0743a3bb16b602bbd7d6492601a5d81a8de274d60cc3b8bdfd25dbc7fa4699cae82c02e5419dca6cad11f05bcdb6acc6290287871d7e94347a
6
+ metadata.gz: 65d4138506168bbf1403791cb00573270781c30c54e9a7bf49b43c8145247188ac19fd7410558f86cc95c46986ecd5d78920725919210d0c24a2a2dbb36556b3
7
+ data.tar.gz: 8f2cc4f9f4f8215cc6b3e3cd0caf8e917608ac95ce3bbcfc6d371678fbaef8399f9cdccdc31cbe379319a244979278201c1607abc642c7c944a8f6275e6ed028
data/.circleci/config.yml CHANGED
@@ -1,45 +1,70 @@
1
1
  version: 2
2
+
3
+ build_config: &build_config
4
+ working_directory: ~/repo
5
+ steps:
6
+ - checkout
7
+
8
+ - restore_cache:
9
+ keys:
10
+ - v1-dependencies-{{ checksum "devise_2fa.gemspec" }}-{{ checksum "Appraisals" }}
11
+ - v1-dependencies-{{ checksum "devise_2fa.gemspec" }}
12
+ - v1-dependencies
13
+
14
+ - run: bin/setup
15
+
16
+ - save_cache:
17
+ paths:
18
+ - ./vendor/bundle
19
+ key: v1-dependencies-{{ checksum "devise_2fa.gemspec" }}-{{ checksum "Appraisals" }}
20
+
21
+ - run:
22
+ name: Setup Symmetric Encryption Config
23
+ command: |
24
+ cd spec/dummy/
25
+ symmetric-encryption -g
26
+
27
+ - run:
28
+ name: Run Tests
29
+ command: |
30
+ mkdir /tmp/test-results
31
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
32
+ circleci tests split --split-by=timings)"
33
+
34
+ bundle exec appraisal rspec \
35
+ --format progress \
36
+ --format RspecJunitFormatter \
37
+ --out /tmp/test-results/rspec.xml \
38
+ --format progress \
39
+ $TEST_FILES
40
+
41
+ - store_test_results:
42
+ path: /tmp/test-results
43
+ - store_artifacts:
44
+ path: /tmp/test-results
45
+ destination: test-results
46
+
2
47
  jobs:
48
+ ruby-2.4:
49
+ <<: *build_config
50
+ docker:
51
+ - image: circleci/ruby:2.4-node-browsers
52
+ - image: circleci/mongo
53
+ ruby-2.5:
54
+ <<: *build_config
55
+ docker:
56
+ - image: circleci/ruby:2.5-node-browsers
57
+ - image: circleci/mongo
3
58
  build:
59
+ <<: *build_config
4
60
  docker:
5
- - image: circleci/ruby:2.4.1-node-browsers
6
-
7
- working_directory: ~/repo
8
-
9
- steps:
10
- - checkout
11
-
12
- - restore_cache:
13
- keys:
14
- - v1-dependencies-{{ checksum "devise_2fa.gemspec" }}
15
-
16
- - run:
17
- name: install dependencies
18
- command: |
19
- bundle install --jobs=4 --retry=3 --path vendor/bundle
20
-
21
- - save_cache:
22
- paths:
23
- - ./vendor/bundle
24
- key: v1-dependencies-{{ checksum "devise_2fa.gemspec" }}
25
-
26
- - run: cd spec/dummy && bin/setup
27
-
28
- - run:
29
- name: run tests
30
- command: |
31
- mkdir /tmp/test-results
32
- TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
33
- circleci tests split --split-by=timings)"
34
-
35
- bundle exec rspec \
36
- --format progress \
37
- --out /tmp/test-results/rspec.xml \
38
- --format progress \
39
- $TEST_FILES
40
-
41
- - store_test_results:
42
- path: /tmp/test-results
43
- - store_artifacts:
44
- path: /tmp/test-results
45
- destination: test-results
61
+ - image: circleci/ruby:2.6-node-browsers
62
+ - image: circleci/mongo
63
+
64
+ workflows:
65
+ version: 2
66
+ build:
67
+ jobs:
68
+ - ruby-2.4
69
+ - ruby-2.5
70
+ - build
data/.gitignore CHANGED
@@ -42,3 +42,6 @@ spec/dummy/db/test.sqlite3
42
42
  *.cache
43
43
  spec/dummy/tmp/development_secret.txt
44
44
  spec/dummy/tmp/restart.txt
45
+
46
+ gemfiles/
47
+ Gemfile.lock
data/Appraisals ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'sqlite3' do
4
+ gem 'rails', '~> 5.2'
5
+ gem 'sqlite3'
6
+ end
7
+
8
+ appraise 'mongodb' do
9
+ gem 'rails', '~> 5.2'
10
+ gem 'mongoid'
11
+ gem 'bson_ext'
12
+ end
data/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2019-08-22
10
+ ### Added
11
+ - Appraisal suite.
12
+ - Mongoid specs.
13
+
14
+ ### Changed
15
+ - Refactors #validate_otp_token_with_drift from #15.
16
+ - Upgraded rotp dependency to ```~> 5.1```.
17
+
18
+ ## [0.3.0] - REVOKED
19
+
9
20
  ## [0.2.1] - 2019-05-28
10
21
  ### Changed
11
22
  - Relaxed Rails dependency to ```~> 6.1```.
data/bin/rspec CHANGED
@@ -1,10 +1,3 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
1
+ #!/bin/bash
3
2
 
4
- begin
5
- load File.expand_path('../spring', __FILE__)
6
- rescue LoadError => e
7
- raise unless e.message.include?('spring')
8
- end
9
- require 'bundler/setup'
10
- load Gem.bin_path('rspec-core', 'rspec')
3
+ bundle exec appraisal rspec
data/bin/setup CHANGED
@@ -9,4 +9,6 @@ echo "Installing dependencies"
9
9
  gem install bundler --conservative
10
10
  bundle check || bundle install
11
11
 
12
- $DIR/../spec/dummy/bin/setup
12
+ bundle exec appraisal install
13
+
14
+ bundle exec appraisal $DIR/../spec/dummy/bin/setup
data/devise_2fa.gemspec CHANGED
@@ -21,14 +21,15 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency "rails", ">= 4.1", "< 6.1"
22
22
 
23
23
  gem.add_runtime_dependency 'devise', '~> 4.0'
24
- gem.add_runtime_dependency 'rotp', '~> 4.1'
24
+ gem.add_runtime_dependency 'rotp', '~> 5.1'
25
25
  gem.add_runtime_dependency 'rqrcode', '~> 0.10.1'
26
26
  gem.add_runtime_dependency 'symmetric-encryption', '~> 4.3.0'
27
27
 
28
- gem.add_development_dependency 'sqlite3', '~> 1.3.6'
29
- gem.add_development_dependency 'selenium-webdriver'
30
- gem.add_development_dependency 'rspec-rails'
28
+ gem.add_development_dependency 'appraisal'
31
29
  gem.add_development_dependency 'capybara'
32
- gem.add_development_dependency 'rspec'
33
30
  gem.add_development_dependency 'pry'
31
+ gem.add_development_dependency 'rspec'
32
+ gem.add_development_dependency 'rspec-rails'
33
+ gem.add_development_dependency 'rspec_junit_formatter'
34
+ gem.add_development_dependency 'selenium-webdriver'
34
35
  end
@@ -1,5 +1,5 @@
1
1
  module Devise
2
2
  module TwoFactor
3
- VERSION = '0.2.1'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -117,10 +117,15 @@ module Devise::Models
117
117
  private
118
118
 
119
119
  def validate_otp_token_with_drift(token)
120
- # should be centered around saved drift
121
- (-self.class.otp_drift_window..self.class.otp_drift_window).any? do |drift|
122
- time_based_otp.verify(token, drift_behind: (0.5 * drift), at: Time.now + (0.5 * drift))
123
- end
120
+ time_based_otp.verify(
121
+ token,
122
+ drift_behind: drift.minutes.seconds,
123
+ at: Time.now + drift.minutes.seconds / 2
124
+ )
125
+ end
126
+
127
+ def drift
128
+ self.class.otp_drift_window
124
129
  end
125
130
 
126
131
  def generate_otp_persistence_seed
@@ -1,3 +1,7 @@
1
- class ApplicationRecord < ActiveRecord::Base
2
- self.abstract_class = true
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveRecord)
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
3
7
  end
@@ -1,6 +1,38 @@
1
- class User < ApplicationRecord
2
- # Include default devise modules. Others available are:
3
- # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
4
- devise :two_factorable, :database_authenticatable, :registerable,
5
- :recoverable, :rememberable, :validatable
1
+ # frozen_string_literal: true
2
+
3
+ require 'symmetric_encryption'
4
+
5
+ if defined?(ActiveRecord)
6
+ class User < ApplicationRecord
7
+ devise :two_factorable, :database_authenticatable, :registerable,
8
+ :recoverable, :rememberable, :validatable
9
+ end
10
+ else
11
+ require 'mongoid'
12
+ class User
13
+ include Mongoid::Document
14
+ field :encrypted_otp_auth_secret, type: String, encrypted: true
15
+ field :encrypted_otp_recovery_secret, type: String, encrypted: true
16
+ field :otp_enabled, type: Boolean, default: false
17
+ field :otp_mandatory, type: Boolean, default: false
18
+ field :otp_enabled_on, type: DateTime
19
+ field :otp_failed_attempts, type: Integer, default: 0
20
+ field :otp_recovery_counter, type: Integer, default: 0
21
+ field :otp_persistence_seed, type: String
22
+ field :otp_session_challenge, type: String
23
+ field :otp_challenge_expires, type: DateTime
24
+
25
+ index({ otp_session_challenge: 1 }, background: true)
26
+ index({ otp_challenge_expires: 1 }, background: true)
27
+
28
+ devise :two_factorable, :database_authenticatable, :registerable,
29
+ :recoverable, :rememberable, :validatable
30
+ field :email, type: String, default: ''
31
+ field :encrypted_password, type: String, default: ''
32
+ field :reset_password_token, type: String
33
+ field :reset_password_sent_at, type: Time
34
+ field :remember_created_at, type: Time
35
+ field :name, type: String
36
+ field :phone, type: String
37
+ end
6
38
  end
@@ -7,9 +7,6 @@
7
7
  <title>Dummy</title>
8
8
  <%= csrf_meta_tags %>
9
9
  <%= csp_meta_tag %>
10
-
11
- <%= stylesheet_link_tag 'application', media: 'all' %>
12
- <%= javascript_include_tag 'application' %>
13
10
  </head>
14
11
 
15
12
  <body>
data/spec/dummy/bin/setup CHANGED
@@ -5,21 +5,6 @@ include FileUtils
5
5
  # path to your application root.
6
6
  APP_ROOT = File.expand_path('..', __dir__)
7
7
 
8
- def system!(*args)
9
- system(*args) || abort("\n== Command #{args} failed ==")
10
- end
11
-
12
8
  chdir APP_ROOT do
13
- puts '== Installing dependencies =='
14
- system! 'gem install bundler --conservative'
15
- system('bundle check') || system!('bundle install')
16
-
17
- puts "\n== Preparing database =="
18
- system! 'bin/rails db:setup'
19
-
20
- puts "\n== Removing old logs and tempfiles =="
21
- system! 'bin/rails log:clear tmp:clear'
22
-
23
- puts "\n== Restarting application server =="
24
- system! 'bin/rails restart'
9
+ system 'bin/rails db:setup 2> /dev/null'
25
10
  end
@@ -1,6 +1,14 @@
1
1
  require_relative 'boot'
2
2
 
3
- require 'rails/all'
3
+ require 'rails'
4
+ require 'active_model/railtie'
5
+ unless ENV['BUNDLE_GEMFILE'].include?('mongodb')
6
+ require 'active_record/railtie'
7
+ end
8
+ require 'action_controller/railtie'
9
+ require 'action_view/railtie'
10
+ require 'sprockets/railtie'
11
+ require 'rails/test_unit/railtie'
4
12
 
5
13
  Bundler.require(*Rails.groups)
6
14
  require 'devise-2fa'
@@ -11,4 +19,3 @@ module Dummy
11
19
  config.load_defaults 5.0
12
20
  end
13
21
  end
14
-
@@ -1,9 +1,3 @@
1
- # SQLite version 3.x
2
- # gem install sqlite3
3
- #
4
- # Ensure the SQLite 3 gem is defined in your Gemfile
5
- # gem 'sqlite3'
6
- #
7
1
  default: &default
8
2
  adapter: sqlite3
9
3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
@@ -19,7 +13,3 @@ development:
19
13
  test:
20
14
  <<: *default
21
15
  database: db/test.sqlite3
22
-
23
- production:
24
- <<: *default
25
- database: db/production.sqlite3
@@ -27,23 +27,9 @@ Rails.application.configure do
27
27
  config.cache_store = :null_store
28
28
  end
29
29
 
30
- # Store uploaded files on the local file system (see config/storage.yml for options)
31
- config.active_storage.service = :local
32
-
33
- # Don't care if the mailer can't send.
34
- config.action_mailer.raise_delivery_errors = false
35
-
36
- config.action_mailer.perform_caching = false
37
-
38
30
  # Print deprecation notices to the Rails logger.
39
31
  config.active_support.deprecation = :log
40
32
 
41
- # Raise an error on page load if there are pending migrations.
42
- config.active_record.migration_error = :page_load
43
-
44
- # Highlight code that triggered database queries in logs.
45
- config.active_record.verbose_query_logs = true
46
-
47
33
  # Debug mode disables concatenation and preprocessing of assets.
48
34
  # This option may cause significant delays in view rendering with a large
49
35
  # number of complex assets.
@@ -10,7 +10,7 @@ Rails.application.configure do
10
10
  # Do not eager load code on boot. This avoids loading your whole application
11
11
  # just for the purpose of running a single test. If you are using a tool that
12
12
  # preloads Rails for running tests, you may have to set it to true.
13
- config.eager_load = false
13
+ config.eager_load = true
14
14
 
15
15
  # Configure public file server for tests with Cache-Control for performance
16
16
  config.public_file_server.enabled = true
@@ -28,19 +28,6 @@ Rails.application.configure do
28
28
  # Disable request forgery protection in test environment
29
29
  config.action_controller.allow_forgery_protection = false
30
30
 
31
- # Store uploaded files on the local file system in a temporary directory
32
- config.active_storage.service = :test
33
-
34
- config.action_mailer.perform_caching = false
35
-
36
- # Tell Action Mailer not to deliver emails to the real world
37
- # The :test delivery method accumulates sent emails in the
38
- # ActionMailer::Base.deliveries array.
39
- config.action_mailer.delivery_method = :test
40
-
41
31
  # Print deprecation notices to the stderr
42
32
  config.active_support.deprecation = :stderr
43
-
44
- # Raises error for missing translations
45
- # config.action_view.raise_on_missing_translations = true
46
33
  end
@@ -30,7 +30,11 @@ Devise.setup do |config|
30
30
  # Load and configure the ORM. Supports :active_record (default) and
31
31
  # :mongoid (bson_ext recommended) by default. Other ORMs may be
32
32
  # available as additional gems.
33
- require 'devise/orm/active_record'
33
+ if defined?(ActiveRecord)
34
+ require 'devise/orm/active_record'
35
+ else
36
+ require 'devise/orm/mongoid'
37
+ end
34
38
 
35
39
  # ==> Configuration for any authentication mechanism
36
40
  # Configure which keys are used when authenticating a user. The default is
@@ -0,0 +1,164 @@
1
+ development:
2
+ # Configure available database clients. (required)
3
+ clients:
4
+ # Defines the default client. (required)
5
+ default:
6
+ # Defines the name of the default database that Mongoid can connect to.
7
+ # (required).
8
+ database: dummy_development
9
+ # Provides the hosts the default client can connect to. Must be an array
10
+ # of host:port pairs. (required)
11
+ hosts:
12
+ - localhost:27017
13
+ options:
14
+ # Note that all options listed below are Ruby driver client options (the mongo gem).
15
+ # Please refer to the driver documentation of the version of the mongo gem you are using
16
+ # for the most up-to-date list of options.
17
+ #
18
+ # Change the default write concern. (default = { w: 1 })
19
+ # write:
20
+ # w: 1
21
+
22
+ # Change the default read preference. Valid options for mode are: :secondary,
23
+ # :secondary_preferred, :primary, :primary_preferred, :nearest
24
+ # (default: primary)
25
+ # read:
26
+ # mode: :secondary_preferred
27
+ # tag_sets:
28
+ # - use: web
29
+
30
+ # The name of the user for authentication.
31
+ # user: 'user'
32
+
33
+ # The password of the user for authentication.
34
+ # password: 'password'
35
+
36
+ # The user's database roles.
37
+ # roles:
38
+ # - 'dbOwner'
39
+
40
+ # Change the default authentication mechanism. Valid options are: :scram,
41
+ # :mongodb_cr, :mongodb_x509, and :plain. Note that all authentication
42
+ # mechanisms require username and password, with the exception of :mongodb_x509.
43
+ # Default on mongoDB 3.0 is :scram, default on 2.4 and 2.6 is :plain.
44
+ # auth_mech: :scram
45
+
46
+ # The database or source to authenticate the user against.
47
+ # (default: the database specified above or admin)
48
+ # auth_source: admin
49
+
50
+ # Force a the driver cluster to behave in a certain manner instead of auto-
51
+ # discovering. Can be one of: :direct, :replica_set, :sharded. Set to :direct
52
+ # when connecting to hidden members of a replica set.
53
+ # connect: :direct
54
+
55
+ # Changes the default time in seconds the server monitors refresh their status
56
+ # via ismaster commands. (default: 10)
57
+ # heartbeat_frequency: 10
58
+
59
+ # The time in seconds for selecting servers for a near read preference. (default: 0.015)
60
+ # local_threshold: 0.015
61
+
62
+ # The timeout in seconds for selecting a server for an operation. (default: 30)
63
+ # server_selection_timeout: 30
64
+
65
+ # The maximum number of connections in the connection pool. (default: 5)
66
+ # max_pool_size: 5
67
+
68
+ # The minimum number of connections in the connection pool. (default: 1)
69
+ # min_pool_size: 1
70
+
71
+ # The time to wait, in seconds, in the connection pool for a connection
72
+ # to be checked in before timing out. (default: 5)
73
+ # wait_queue_timeout: 5
74
+
75
+ # The time to wait to establish a connection before timing out, in seconds.
76
+ # (default: 5)
77
+ # connect_timeout: 5
78
+
79
+ # The timeout to wait to execute operations on a socket before raising an error.
80
+ # (default: 5)
81
+ # socket_timeout: 5
82
+
83
+ # The name of the replica set to connect to. Servers provided as seeds that do
84
+ # not belong to this replica set will be ignored.
85
+ # replica_set: name
86
+
87
+ # Whether to connect to the servers via ssl. (default: false)
88
+ # ssl: true
89
+
90
+ # The certificate file used to identify the connection against MongoDB.
91
+ # ssl_cert: /path/to/my.cert
92
+
93
+ # The private keyfile used to identify the connection against MongoDB.
94
+ # Note that even if the key is stored in the same file as the certificate,
95
+ # both need to be explicitly specified.
96
+ # ssl_key: /path/to/my.key
97
+
98
+ # A passphrase for the private key.
99
+ # ssl_key_pass_phrase: password
100
+
101
+ # Whether or not to do peer certification validation. (default: true)
102
+ # ssl_verify: true
103
+
104
+ # The file containing a set of concatenated certification authority certifications
105
+ # used to validate certs passed from the other end of the connection.
106
+ # ssl_ca_cert: /path/to/ca.cert
107
+
108
+
109
+ # Configure Mongoid specific options. (optional)
110
+ options:
111
+ # Includes the root model name in json serialization. (default: false)
112
+ # include_root_in_json: false
113
+
114
+ # Include the _type field in serialization. (default: false)
115
+ # include_type_for_serialization: false
116
+
117
+ # Preload all models in development, needed when models use
118
+ # inheritance. (default: false)
119
+ # preload_models: false
120
+
121
+ # Raise an error when performing a #find and the document is not found.
122
+ # (default: true)
123
+ # raise_not_found_error: true
124
+
125
+ # Raise an error when defining a scope with the same name as an
126
+ # existing method. (default: false)
127
+ # scope_overwrite_exception: false
128
+
129
+ # Raise an error when defining a field with the same name as an
130
+ # existing method. (default: false)
131
+ # duplicate_fields_exception: false
132
+
133
+ # Use Active Support's time zone in conversions. (default: true)
134
+ # use_activesupport_time_zone: true
135
+
136
+ # Ensure all times are UTC in the app side. (default: false)
137
+ # use_utc: false
138
+
139
+ # Set the Mongoid and Ruby driver log levels when not in a Rails
140
+ # environment. The Mongoid logger will be set to the Rails logger
141
+ # otherwise.(default: :info)
142
+ # log_level: :info
143
+
144
+ # Control whether `belongs_to` association is required. By default
145
+ # `belongs_to` will trigger a validation error if the association
146
+ # is not present. (default: true)
147
+ # belongs_to_required_by_default: true
148
+
149
+ # Application name that is printed to the mongodb logs upon establishing a
150
+ # connection in server versions >= 3.4. Note that the name cannot exceed 128 bytes.
151
+ # app_name: MyApplicationName
152
+
153
+ # Use background indexes by default if `background` option not specified. (default: false)
154
+ # background_indexing: false
155
+ test:
156
+ clients:
157
+ default:
158
+ database: dummy_test
159
+ hosts:
160
+ - localhost:27017
161
+ options:
162
+ read:
163
+ mode: :primary
164
+ max_pool_size: 1
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative Rails.root.join('app/models/user')
4
+
5
+
1
6
  Rails.application.routes.draw do
2
7
  devise_for :users
3
- root to: "application#show"
8
+ root to: 'application#show'
4
9
  end
@@ -1,4 +1,4 @@
1
- class DeviseTwoFactorAddToUsers < ActiveRecord::Migration[5.0]
1
+ class DeviseTwoFactorAddToUsers < ActiveRecord::Migration
2
2
  def self.up
3
3
  change_table :users do |t|
4
4
  t.string :otp_auth_secret
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe User, type: :model do
6
- subject (:user) { User.new(email: 'mb@geemail.com', password: 'iwantabigmac1') }
6
+ subject(:user) { User.create(email: 'mb@geemail.com', password: 'iwantabigmac1') }
7
7
  it 'is valid' do
8
8
  expect(user).to be_valid
9
9
  end
@@ -23,11 +23,56 @@ RSpec.describe User, type: :model do
23
23
  end
24
24
 
25
25
  describe '#password' do
26
- subject(:user) { User.new(email: 'mb@geemail.com')}
26
+ subject(:user) { User.new(email: 'mb@geemail.com') }
27
27
 
28
28
  it 'is required' do
29
29
  expect(user).to be_invalid
30
30
  end
31
31
  end
32
32
  end
33
+
34
+ describe '#validate_otp_token' do
35
+ context 'when otp_drift_window is 3 minutes (default)' do
36
+ let(:user_rotp) { ROTP::TOTP.new(user.otp_auth_secret) }
37
+ let(:control_time) { Time.now }
38
+ let(:token_time) { control_time }
39
+
40
+ before do
41
+ Devise.otp_drift_window = 3
42
+ allow(Time).to receive(:now).and_return(control_time)
43
+ end
44
+
45
+ context "when token's time is 2 minutes before current time" do
46
+ let(:token_time) { control_time - 2.minutes }
47
+
48
+ it do
49
+ expect(user.validate_otp_token(user_rotp.at(token_time))).to be_falsey
50
+ end
51
+ end
52
+
53
+ context "when token's time is 1 minute before current time" do
54
+ let(:token_time) { control_time - 1.minute }
55
+
56
+ it do
57
+ expect(user.validate_otp_token(user_rotp.at(token_time))).to be_truthy
58
+ end
59
+ end
60
+
61
+ context "when token's time is 1 minute after current time" do
62
+ let(:token_time) { control_time + 1.minute }
63
+
64
+ it do
65
+ expect(user.validate_otp_token(user_rotp.at(token_time))).to be_truthy
66
+ end
67
+ end
68
+
69
+ context "when token's time is 2 minutes after current time" do
70
+ let(:token_time) { control_time + 2.minute }
71
+
72
+ it do
73
+ expect(user.validate_otp_token(user_rotp.at(token_time))).to be_falsey
74
+ end
75
+ end
76
+ end
77
+ end
33
78
  end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ENV["RAILS_ENV"] ||= "test"
3
+ ENV['RAILS_ENV'] ||= 'test'
4
4
 
5
- require "rails/all"
6
- require "dummy/config/application"
5
+ require 'dummy/config/application'
7
6
  require 'bundler/setup'
8
7
  require 'rspec/rails'
9
8
 
@@ -24,6 +23,11 @@ RSpec.configure do |config|
24
23
  config.before(:each, type: :system) do
25
24
  driven_by :rack_test
26
25
  end
26
+ if defined? Mongoid
27
+ config.before :each do
28
+ Mongoid.purge!
29
+ end
30
+ end
27
31
  end
28
32
 
29
33
 
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe 'Tokens' do
6
- subject (:user) { User.create(email: 'mb@geemail.com', password: 'iwantabigmac1') }
6
+ subject(:user) { User.create(email: 'mb@geemail.com', password: 'iwantabigmac1') }
7
7
 
8
8
  it 'can be disabled by a user after successfully enabling' do
9
9
  enable_otp_and_sign_in user
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-2fa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - William A. Todd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-28 00:00:00.000000000 Z
11
+ date: 2019-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '4.1'
53
+ version: '5.1'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '4.1'
60
+ version: '5.1'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rqrcode
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -87,21 +87,21 @@ dependencies:
87
87
  - !ruby/object:Gem::Version
88
88
  version: 4.3.0
89
89
  - !ruby/object:Gem::Dependency
90
- name: sqlite3
90
+ name: appraisal
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - "~>"
93
+ - - ">="
94
94
  - !ruby/object:Gem::Version
95
- version: 1.3.6
95
+ version: '0'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - "~>"
100
+ - - ">="
101
101
  - !ruby/object:Gem::Version
102
- version: 1.3.6
102
+ version: '0'
103
103
  - !ruby/object:Gem::Dependency
104
- name: selenium-webdriver
104
+ name: capybara
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - ">="
@@ -115,7 +115,7 @@ dependencies:
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  - !ruby/object:Gem::Dependency
118
- name: rspec-rails
118
+ name: pry
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - ">="
@@ -129,7 +129,7 @@ dependencies:
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
131
  - !ruby/object:Gem::Dependency
132
- name: capybara
132
+ name: rspec
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - ">="
@@ -143,7 +143,7 @@ dependencies:
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0'
145
145
  - !ruby/object:Gem::Dependency
146
- name: rspec
146
+ name: rspec-rails
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - ">="
@@ -157,7 +157,21 @@ dependencies:
157
157
  - !ruby/object:Gem::Version
158
158
  version: '0'
159
159
  - !ruby/object:Gem::Dependency
160
- name: pry
160
+ name: rspec_junit_formatter
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ - !ruby/object:Gem::Dependency
174
+ name: selenium-webdriver
161
175
  requirement: !ruby/object:Gem::Requirement
162
176
  requirements:
163
177
  - - ">="
@@ -181,6 +195,7 @@ files:
181
195
  - ".gitignore"
182
196
  - ".hound.yml"
183
197
  - ".ruby-style.yml"
198
+ - Appraisals
184
199
  - CHANGELOG.md
185
200
  - Gemfile
186
201
  - LICENSE
@@ -217,10 +232,6 @@ files:
217
232
  - lib/generators/devise_two_factor/views_generator.rb
218
233
  - lib/generators/mongoid/devise_two_factor_generator.rb
219
234
  - spec/dummy/Rakefile
220
- - spec/dummy/app/assets/images/.keep
221
- - spec/dummy/app/assets/javascripts/application.js
222
- - spec/dummy/app/assets/javascripts/channels/.keep
223
- - spec/dummy/app/assets/stylesheets/application.css
224
235
  - spec/dummy/app/controllers/application_controller.rb
225
236
  - spec/dummy/app/controllers/concerns/.keep
226
237
  - spec/dummy/app/helpers/application_helper.rb
@@ -253,12 +264,13 @@ files:
253
264
  - spec/dummy/config/locales/devise.en.yml
254
265
  - spec/dummy/config/locales/devise.two_factor.en.yml
255
266
  - spec/dummy/config/locales/en.yml
267
+ - spec/dummy/config/mongoid.yml
256
268
  - spec/dummy/config/puma.rb
257
269
  - spec/dummy/config/routes.rb
258
270
  - spec/dummy/config/spring.rb
259
271
  - spec/dummy/config/storage.yml
260
272
  - spec/dummy/db/migrate/20190311184605_devise_create_users.rb
261
- - spec/dummy/db/migrate/20190312222952_devise_two_factor_add_to_users.rb
273
+ - spec/dummy/db/migrate/20190625052821_devise_two_factor_add_to_users.rb
262
274
  - spec/dummy/db/schema.rb
263
275
  - spec/dummy/lib/assets/.keep
264
276
  - spec/dummy/package.json
@@ -294,16 +306,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
306
  - !ruby/object:Gem::Version
295
307
  version: '0'
296
308
  requirements: []
297
- rubygems_version: 3.0.2
309
+ rubygems_version: 3.0.4
298
310
  signing_key:
299
311
  specification_version: 4
300
312
  summary: Includes ActiveRecord and Mongoid ORM support
301
313
  test_files:
302
314
  - spec/dummy/Rakefile
303
- - spec/dummy/app/assets/images/.keep
304
- - spec/dummy/app/assets/javascripts/application.js
305
- - spec/dummy/app/assets/javascripts/channels/.keep
306
- - spec/dummy/app/assets/stylesheets/application.css
307
315
  - spec/dummy/app/controllers/application_controller.rb
308
316
  - spec/dummy/app/controllers/concerns/.keep
309
317
  - spec/dummy/app/helpers/application_helper.rb
@@ -336,12 +344,13 @@ test_files:
336
344
  - spec/dummy/config/locales/devise.en.yml
337
345
  - spec/dummy/config/locales/devise.two_factor.en.yml
338
346
  - spec/dummy/config/locales/en.yml
347
+ - spec/dummy/config/mongoid.yml
339
348
  - spec/dummy/config/puma.rb
340
349
  - spec/dummy/config/routes.rb
341
350
  - spec/dummy/config/spring.rb
342
351
  - spec/dummy/config/storage.yml
343
352
  - spec/dummy/db/migrate/20190311184605_devise_create_users.rb
344
- - spec/dummy/db/migrate/20190312222952_devise_two_factor_add_to_users.rb
353
+ - spec/dummy/db/migrate/20190625052821_devise_two_factor_add_to_users.rb
345
354
  - spec/dummy/db/schema.rb
346
355
  - spec/dummy/lib/assets/.keep
347
356
  - spec/dummy/package.json
File without changes
@@ -1,3 +0,0 @@
1
- //= require rails-ujs
2
- //= require activestorage
3
- //= require_tree .
File without changes
@@ -1,15 +0,0 @@
1
- /*
2
- * This is a manifest file that'll be compiled into application.css, which will include all the files
3
- * listed below.
4
- *
5
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
- * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
- *
8
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
- * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
- * files in this directory. Styles in this file should be added after the last require_* statement.
11
- * It is generally better to create a new file per style scope.
12
- *
13
- *= require_tree .
14
- *= require_self
15
- */