rose_quartz 0.1.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/little-bobby-tables/rose_quartz.svg?branch=master)](https://travis-ci.org/little-bobby-tables/rose_quartz)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/little-bobby-tables/rose_quartz/badges/coverage.svg)](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
|