rose_quartz 0.1.0 → 0.9.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/.travis.yml +2 -0
- data/README.md +100 -4
- data/lib/generators/rose_quartz/install_generator.rb +24 -0
- data/lib/generators/rose_quartz/templates/initializer.rb +16 -0
- data/lib/generators/rose_quartz/templates/locale.en.yml +4 -0
- data/lib/generators/rose_quartz/templates/migration.rb +14 -0
- data/lib/rose_quartz.rb +5 -1
- data/lib/rose_quartz/configuration.rb +5 -12
- data/lib/rose_quartz/devise/controllers/registrations_controller_extensions.rb +88 -0
- data/lib/rose_quartz/devise/controllers/sessions_controller_extensions.rb +13 -0
- data/lib/rose_quartz/devise/strategies/two_factor_authenticatable.rb +18 -5
- data/lib/rose_quartz/hooks.rb +15 -0
- data/lib/rose_quartz/user_authenticator.rb +41 -11
- data/lib/rose_quartz/version.rb +1 -1
- data/rose_quartz.gemspec +7 -3
- metadata +71 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 715723d37a50e4966319aead3e66cf26168a2302
|
4
|
+
data.tar.gz: cbf6814f33a0b4e797f71c13bbfe4deab4c355dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4c51661f7211f4150ba8d89eb1410885d5ba01c3cd79ef72a2be5cc411f088aeb383e0f832c7692a52008d1a3698503aaa10366f1388a2a9eb998daf1128b48
|
7
|
+
data.tar.gz: 43bdea2ee92e8ba06d0d0a3132f17e932bc202ae9716b64b02c70167b6b204b6a6760aa43d911404258f7ade1a6c011dfe096531fb1a0e914db7399b9c76f39e
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# RoseQuartz
|
2
2
|
|
3
3
|
[](https://travis-ci.org/little-bobby-tables/rose_quartz)
|
4
|
+
[](https://codeclimate.com/github/little-bobby-tables/rose_quartz/coverage)
|
4
5
|
|
5
6
|
A gem that adds two-factor authentication (time-based one-time passwords) to [Devise](https://github.com/plataformatec/devise)
|
6
7
|
using the [rotp](https://github.com/mdp/rotp) library.
|
@@ -8,10 +9,105 @@ using the [rotp](https://github.com/mdp/rotp) library.
|
|
8
9
|
It attempts to stay lightweight by making a lot of assumptions — for example, that
|
9
10
|
you have a single authenticatable resource, `User`, and that you're using `ActiveRecord`.
|
10
11
|
|
11
|
-
Highlights
|
12
|
+
#### Highlights:
|
12
13
|
|
13
|
-
*
|
14
|
-
*
|
14
|
+
* Adds optional TOTP (compatible with Google Authenticator) to the sign-in process.
|
15
|
+
* Provides a backup code as a fallback option; resets it once it has been used and notifies the user.
|
16
|
+
* Does not tamper with the `User` model — no additional fields, no included modules.
|
17
|
+
* Employs a separate table that can be updated in future without affecting your codebase and data.
|
15
18
|
* Built with Rails 5 and Devise 4 in mind.
|
16
19
|
|
17
|
-
|
20
|
+
#### What it does not do:
|
21
|
+
|
22
|
+
Use a multiple-page login system (email and password first, two-factor authentication token next).
|
23
|
+
This introduces lots of needless complexity, which goes against the purpose of the gem.
|
24
|
+
|
25
|
+
#### What it should do, but does not (yet):
|
26
|
+
|
27
|
+
* Encrypt the backup code and the secret used to generate OTP.
|
28
|
+
|
29
|
+
## Getting Started
|
30
|
+
|
31
|
+
First, add *RoseQuartz* to your Gemfile:
|
32
|
+
|
33
|
+
```
|
34
|
+
gem 'rose_quartz'
|
35
|
+
```
|
36
|
+
And run:
|
37
|
+
```
|
38
|
+
bundle install
|
39
|
+
```
|
40
|
+
|
41
|
+
Next, you need to copy initializers, locales, and add a migration:
|
42
|
+
```
|
43
|
+
rails g rose_quarts:install
|
44
|
+
```
|
45
|
+
|
46
|
+
Finally, run the migration:
|
47
|
+
```
|
48
|
+
rails db:migrate
|
49
|
+
```
|
50
|
+
|
51
|
+
## Adding views
|
52
|
+
|
53
|
+
#### Signing in
|
54
|
+
|
55
|
+
You need a special field for one-time password/backup code on the sign-in page (*app/views/devise/sessions/new.html.erb*).
|
56
|
+
|
57
|
+
Here's an example:
|
58
|
+
|
59
|
+
```
|
60
|
+
<%# E-mail and password fields %>
|
61
|
+
|
62
|
+
<div class="field">
|
63
|
+
<%= label_tag :otp, 'Two-factor authentication token' %>
|
64
|
+
<%= text_field_tag :otp, '', autocomplete: "off" %>
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<%# The rest of the form %>
|
68
|
+
```
|
69
|
+
|
70
|
+
Note that you must leave the parameter name (`otp`) intact.
|
71
|
+
|
72
|
+
#### Enabling/disabling two-factor authentication
|
73
|
+
|
74
|
+
The gem adds a special extension to Devise that allows you to
|
75
|
+
include two-factor authentication setup in the account editing page
|
76
|
+
(*app/views/devise/registrations/edit.html.erb*).
|
77
|
+
|
78
|
+
As with other settings there, a password is required to toggle two-factor authentication.
|
79
|
+
The user also needs to provide a correct token generated by their TOTP application of choice,
|
80
|
+
which ensures that their device clock is in sync with the server.
|
81
|
+
|
82
|
+
Here's a sample implementation:
|
83
|
+
|
84
|
+
```
|
85
|
+
<div class="field">
|
86
|
+
<%= fields_for :two_factor_authentication do |tfa| %>
|
87
|
+
<% if two_factor_authentication_enabled? %>
|
88
|
+
<%= tfa.label :disable, 'Disable two-factor authentication' %>
|
89
|
+
<%= tfa.check_box :disable %>
|
90
|
+
<p>
|
91
|
+
Your backup code is <strong><%= two_factor_authentication_backup_code %></strong> -
|
92
|
+
save it to access your account if you ever lose your device or don't have it with you.
|
93
|
+
</p>
|
94
|
+
<%= tfa.label :reset_backup_code %>
|
95
|
+
<%= tfa.check_box :reset_backup_code %>
|
96
|
+
<% else %>
|
97
|
+
<%= tfa.hidden_field :secret, value: two_factor_authentication_secret %>
|
98
|
+
<%= image_tag two_factor_authentication_qr_code_uri(size: 200) %>
|
99
|
+
<p>
|
100
|
+
Scan this QR code with your device and enter the token below:
|
101
|
+
</p>
|
102
|
+
<%= tfa.label :token, 'Token' %><br />
|
103
|
+
<%= tfa.text_field :token, value: '' %>
|
104
|
+
<p>
|
105
|
+
Tip: to configure authentication on multiple devices, scan the code using each device.
|
106
|
+
</p>
|
107
|
+
<% end %>
|
108
|
+
<% end %>
|
109
|
+
</div>
|
110
|
+
```
|
111
|
+
|
112
|
+
The following helper methods are available in the view: `two_factor_authentication_enabled?`,
|
113
|
+
`two_factor_authentication_backup_code`, `two_factor_authentication_qr_code_uri`, `two_factor_authentication_secret`.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RoseQuartz
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
include Rails::Generators::Migration
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
copy_file 'initializer.rb', 'config/initializers/rose_quartz.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_locale
|
12
|
+
copy_file 'locale.en.yml', 'config/locales/rose_quartz.en.yml'
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy_migration
|
16
|
+
migration_template 'migration.rb', 'db/migrate/add_two_factor_auth_with_rose_quartz.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.next_migration_number(path)
|
20
|
+
next_migration_number = current_migration_number(path) + 1
|
21
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
RoseQuartz.initialize! do |config|
|
2
|
+
# Token issuer is used as a title in user applications (e.g. Google Authenticator).
|
3
|
+
# It is included in the QR code, and changing it won't have an effect on users
|
4
|
+
# that already have two-factor authentication enabled.
|
5
|
+
config.issuer = 'My Rails Application'
|
6
|
+
|
7
|
+
# In addition to +issuer+, client-side applications display an identifier
|
8
|
+
# (usually, this is account's email address).
|
9
|
+
# This setting needs to refer to an existing attribute or method of the authenticatable model.
|
10
|
+
config.user_identifier = :email
|
11
|
+
|
12
|
+
# Some users may have their devices slightly ahead or behind of the actual time.
|
13
|
+
# To counter this, the authenticator will accept tokens that are generated for
|
14
|
+
# timestamps withing the time drift window defined below.
|
15
|
+
config.time_drift = 2.minutes
|
16
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
en:
|
2
|
+
rose_quartz:
|
3
|
+
backup_code_used: "The backup code you have used to sign in has been reset. Please go to your account settings to copy the new one or temporarily disable two-factor authentication."
|
4
|
+
invalid_token_when_enabling_tfa: "Invalid token. Please make sure that your device has the correct time settings."
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AddTwoFactorAuthWithRoseQuartz < ActiveRecord::Migration[5.0]
|
2
|
+
def up
|
3
|
+
create_table :user_authenticators do |t|
|
4
|
+
t.integer :user_id
|
5
|
+
t.string :secret
|
6
|
+
t.string :backup_code
|
7
|
+
t.integer :last_authenticated_at
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def down
|
12
|
+
drop_table :user_authenticators
|
13
|
+
end
|
14
|
+
end
|
data/lib/rose_quartz.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
require 'devise'
|
3
3
|
|
4
4
|
require 'rose_quartz/version'
|
5
|
+
|
6
|
+
require 'rose_quartz/hooks'
|
5
7
|
require 'rose_quartz/configuration'
|
6
8
|
require 'rose_quartz/user_authenticator'
|
7
9
|
require 'rose_quartz/devise/strategies/two_factor_authenticatable'
|
10
|
+
require 'rose_quartz/devise/controllers/sessions_controller_extensions'
|
11
|
+
require 'rose_quartz/devise/controllers/registrations_controller_extensions'
|
@@ -8,23 +8,16 @@ module RoseQuartz
|
|
8
8
|
|
9
9
|
def self.initialize!
|
10
10
|
yield self.configuration
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.insert_authentication_strategy!
|
15
|
-
::Devise.setup do |c|
|
16
|
-
c.warden do |manager|
|
17
|
-
manager.default_strategies(scope: :user).unshift :two_factor_authenticatable
|
18
|
-
end
|
19
|
-
end
|
11
|
+
Hooks.initialize_hooks!
|
20
12
|
end
|
21
13
|
|
22
14
|
class Configuration
|
23
|
-
attr_accessor :issuer, :time_drift
|
15
|
+
attr_accessor :issuer, :time_drift, :user_identifier
|
24
16
|
|
25
17
|
def initialize
|
26
|
-
@issuer = ''
|
27
|
-
@time_drift =
|
18
|
+
@issuer = 'RoseQuartz'
|
19
|
+
@time_drift = 1.minute
|
20
|
+
@user_identifier = :email
|
28
21
|
end
|
29
22
|
end
|
30
23
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'i18n'
|
3
|
+
require 'rqrcode'
|
4
|
+
|
5
|
+
module Devise
|
6
|
+
module RegistrationsControllerExtensions
|
7
|
+
def self.prepended(base)
|
8
|
+
base.class_eval do
|
9
|
+
helper_method :two_factor_authentication_enabled?,
|
10
|
+
:two_factor_authentication_backup_code,
|
11
|
+
:two_factor_authentication_qr_code_uri,
|
12
|
+
:two_factor_authentication_secret
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_resource(resource, _params)
|
17
|
+
result = super
|
18
|
+
edit_two_factor_authentication(resource) if result
|
19
|
+
result
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
# View helpers
|
25
|
+
|
26
|
+
def two_factor_authentication_enabled?(user = resource)
|
27
|
+
RoseQuartz::UserAuthenticator.exists? user_id: user.id
|
28
|
+
end
|
29
|
+
|
30
|
+
def two_factor_authentication_backup_code
|
31
|
+
authenticator(resource).backup_code
|
32
|
+
end
|
33
|
+
|
34
|
+
def two_factor_authentication_qr_code_uri(size:)
|
35
|
+
uri = authenticator.provisioning_uri
|
36
|
+
qr = RQRCode::QRCode.new(uri)
|
37
|
+
qr.as_png(size: size).to_data_url
|
38
|
+
end
|
39
|
+
|
40
|
+
def two_factor_authentication_secret
|
41
|
+
authenticator.secret
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Internal logic
|
47
|
+
|
48
|
+
def edit_two_factor_authentication(resource)
|
49
|
+
if two_factor_authentication_enabled?
|
50
|
+
disable_two_factor_authentication!(resource) if form_params[:disable] == '1'
|
51
|
+
reset_two_factor_authentication_backup_code!(resource) if form_params[:reset_backup_code] == '1'
|
52
|
+
else
|
53
|
+
enable_two_factor_authentication!(resource)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def disable_two_factor_authentication!(resource)
|
58
|
+
authenticator(resource).disable!
|
59
|
+
end
|
60
|
+
|
61
|
+
def reset_two_factor_authentication_backup_code!(resource)
|
62
|
+
authenticator(resource).reset_backup_code!
|
63
|
+
end
|
64
|
+
|
65
|
+
def enable_two_factor_authentication!(resource)
|
66
|
+
secret, token = form_params.values_at(:secret, :token)
|
67
|
+
authenticator = RoseQuartz::UserAuthenticator.new(user: resource, secret: secret)
|
68
|
+
token_valid = authenticator.authenticate_otp!(token) rescue false
|
69
|
+
if token_valid
|
70
|
+
authenticator.save
|
71
|
+
else
|
72
|
+
resource.errors.add(:base, I18n.t('rose_quartz.invalid_token_when_enabling_tfa'))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def form_params
|
77
|
+
params.require(:two_factor_authentication).permit(:secret, :token, :disable, :reset_backup_code)
|
78
|
+
end
|
79
|
+
|
80
|
+
def authenticator(existing_user = nil)
|
81
|
+
@authenticator ||= if existing_user
|
82
|
+
RoseQuartz::UserAuthenticator.find_by(user_id: resource.id)
|
83
|
+
else
|
84
|
+
RoseQuartz::UserAuthenticator.new(user: resource)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module SessionsControllerExtensions
|
5
|
+
def create
|
6
|
+
super do |_resource|
|
7
|
+
if request.env['rose_quartz.backup_code_used']
|
8
|
+
flash[:alert] = t('rose_quartz.backup_code_used')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -7,17 +7,30 @@ module Devise
|
|
7
7
|
def authenticate!
|
8
8
|
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
|
9
9
|
|
10
|
-
super if validate(resource) {
|
10
|
+
super if validate(resource) { authenticated?(resource) }
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def authenticated?(resource)
|
14
14
|
authenticator = RoseQuartz::UserAuthenticator.find_by(user_id: resource.id)
|
15
|
-
|
15
|
+
token = params['otp']
|
16
16
|
|
17
|
-
|
17
|
+
# Two-factor authentication is disabled
|
18
|
+
return true if authenticator.nil?
|
19
|
+
|
20
|
+
# Token is not provided
|
18
21
|
return false if token.nil?
|
19
22
|
|
20
|
-
|
23
|
+
# Token is a valid OTP
|
24
|
+
return true if authenticator.authenticate_otp!(token)
|
25
|
+
|
26
|
+
# Token is a valid backup code
|
27
|
+
if authenticator.authenticate_backup_code!(token)
|
28
|
+
env['rose_quartz.backup_code_used'] = true
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
|
32
|
+
# Token is not a valid OTP or backup code
|
33
|
+
false
|
21
34
|
end
|
22
35
|
end
|
23
36
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module RoseQuartz
|
3
|
+
module Hooks
|
4
|
+
def self.initialize_hooks!
|
5
|
+
::Devise.setup do |c|
|
6
|
+
c.warden do |manager|
|
7
|
+
manager.default_strategies(scope: :user).unshift :two_factor_authenticatable
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
::Devise::SessionsController.prepend Devise::SessionsControllerExtensions
|
12
|
+
::Devise::RegistrationsController.prepend Devise::RegistrationsControllerExtensions
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -5,22 +5,52 @@ module RoseQuartz
|
|
5
5
|
class UserAuthenticator < ::ActiveRecord::Base
|
6
6
|
belongs_to :user
|
7
7
|
|
8
|
-
|
8
|
+
after_initialize :set_secret_and_backup_code, if: :new_record?
|
9
9
|
|
10
|
-
def
|
11
|
-
self.secret
|
10
|
+
def set_secret_and_backup_code
|
11
|
+
self.secret ||= ROTP::Base32.random_base32
|
12
|
+
self.backup_code ||= generate_backup_code
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def authenticate_otp!(token)
|
16
|
+
authenticated_at = totp.verify_with_drift_and_prior(
|
17
|
+
token, RoseQuartz.configuration.time_drift, last_authenticated_at)
|
18
|
+
if authenticated_at
|
19
|
+
update_columns last_authenticated_at: authenticated_at if persisted?
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
26
|
+
def authenticate_backup_code!(token)
|
27
|
+
if token == backup_code
|
28
|
+
reset_backup_code!
|
29
|
+
true
|
30
|
+
else
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_backup_code!
|
36
|
+
update_columns backup_code: generate_backup_code
|
37
|
+
end
|
38
|
+
|
39
|
+
def totp
|
40
|
+
@authenticator ||= ROTP::TOTP.new(secret, issuer: RoseQuartz.configuration.issuer)
|
41
|
+
end
|
42
|
+
|
43
|
+
def provisioning_uri
|
44
|
+
totp.provisioning_uri(user.send(RoseQuartz.configuration.user_identifier))
|
45
|
+
end
|
46
|
+
|
47
|
+
alias disable! delete
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Four groups of 4-character base32 strings joined by dashes, e.g. "gs3w-ntpt-hrse-v23t"
|
52
|
+
def generate_backup_code
|
53
|
+
ROTP::Base32.random_base32(16).scan(/.{1,4}/).join('-')
|
24
54
|
end
|
25
55
|
end
|
26
56
|
end
|
data/lib/rose_quartz/version.rb
CHANGED
data/rose_quartz.gemspec
CHANGED
@@ -16,11 +16,15 @@ Gem::Specification.new do |spec|
|
|
16
16
|
end
|
17
17
|
spec.require_paths = ['lib']
|
18
18
|
|
19
|
-
spec.add_runtime_dependency 'rails', '
|
20
|
-
spec.add_runtime_dependency 'devise', '
|
21
|
-
spec.add_runtime_dependency 'rotp', '
|
19
|
+
spec.add_runtime_dependency 'rails', '>= 5.0'
|
20
|
+
spec.add_runtime_dependency 'devise', '>= 4.2'
|
21
|
+
spec.add_runtime_dependency 'rotp', '>= 3.3'
|
22
|
+
spec.add_runtime_dependency 'rqrcode', '>= 0.10'
|
22
23
|
|
23
24
|
spec.add_development_dependency 'sqlite3'
|
25
|
+
spec.add_development_dependency 'capybara'
|
24
26
|
spec.add_development_dependency 'factory_girl_rails'
|
25
27
|
spec.add_development_dependency 'minitest-reporters'
|
28
|
+
spec.add_development_dependency 'simplecov'
|
29
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
26
30
|
end
|
metadata
CHANGED
@@ -1,57 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rose_quartz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- little-bobby-tables
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: devise
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rotp
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '3.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rqrcode
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: sqlite3
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: capybara
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: factory_girl_rails
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +122,34 @@ dependencies:
|
|
94
122
|
- - ">="
|
95
123
|
- !ruby/object:Gem::Version
|
96
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: codeclimate-test-reporter
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
97
153
|
description:
|
98
154
|
email:
|
99
155
|
- little-bobby-tables@users.noreply.github.com
|
@@ -107,9 +163,16 @@ files:
|
|
107
163
|
- LICENSE
|
108
164
|
- README.md
|
109
165
|
- Rakefile
|
166
|
+
- lib/generators/rose_quartz/install_generator.rb
|
167
|
+
- lib/generators/rose_quartz/templates/initializer.rb
|
168
|
+
- lib/generators/rose_quartz/templates/locale.en.yml
|
169
|
+
- lib/generators/rose_quartz/templates/migration.rb
|
110
170
|
- lib/rose_quartz.rb
|
111
171
|
- lib/rose_quartz/configuration.rb
|
172
|
+
- lib/rose_quartz/devise/controllers/registrations_controller_extensions.rb
|
173
|
+
- lib/rose_quartz/devise/controllers/sessions_controller_extensions.rb
|
112
174
|
- lib/rose_quartz/devise/strategies/two_factor_authenticatable.rb
|
175
|
+
- lib/rose_quartz/hooks.rb
|
113
176
|
- lib/rose_quartz/user_authenticator.rb
|
114
177
|
- lib/rose_quartz/version.rb
|
115
178
|
- rose_quartz.gemspec
|