two_factor_authentication 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +41 -2
- data/app/controllers/devise/two_factor_authentication_controller.rb +1 -1
- data/config/locales/ru.yml +1 -0
- data/lib/generators/active_record/two_factor_authentication_generator.rb +1 -1
- data/lib/two_factor_authentication.rb +5 -0
- data/lib/two_factor_authentication/controllers/helpers.rb +3 -3
- data/lib/two_factor_authentication/hooks/two_factor_authenticatable.rb +1 -1
- data/lib/two_factor_authentication/models/two_factor_authenticatable.rb +4 -4
- data/lib/two_factor_authentication/version.rb +1 -1
- data/spec/controllers/two_factor_auth_spec.rb +3 -5
- data/spec/features/two_factor_authenticatable_spec.rb +5 -3
- data/spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb +12 -8
- data/spec/rails_app/app/views/home/dashboard.html.erb +4 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/features_spec_helper.rb +1 -1
- data/two_factor_authentication.gemspec +2 -2
- metadata +9 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb84a503671c99963674feb9a48b9e0b4078c457
|
4
|
+
data.tar.gz: e1004e05b1adfb4fc9dbae58638f3091a29cd4c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f790fd330b74c0bed8199477a23dce4d52d5035c86495a62d2d5e5b0bc77f5d97c4cea8b6b9aee5a6b3b306d9d45664fb95b5a0c3ca89838b3a832302de1590
|
7
|
+
data.tar.gz: 48d79bca0d7eb2f51ed606d4c34aea8b7f7d01cf0c2e59e763a0fd6eaad167dbc53c4c7db495bc0931fa6b061c4a095a6d521b868334eeaf77267538861b3cf6
|
data/Gemfile
CHANGED
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
|
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
|
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)[
|
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
|
data/config/locales/ru.yml
CHANGED
@@ -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)[
|
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(:[],
|
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])[
|
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 = {})
|
@@ -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.
|
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.
|
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(
|
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(
|
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
|
153
|
+
expect(instance.max_login_attempts?).to be_falsey
|
150
154
|
instance.second_factor_attempts_count = 0
|
151
|
-
expect(instance.max_login_attempts?).to
|
155
|
+
expect(instance.max_login_attempts?).to be_falsey
|
152
156
|
instance.second_factor_attempts_count = 1
|
153
|
-
expect(instance.max_login_attempts?).to
|
157
|
+
expect(instance.max_login_attempts?).to be_falsey
|
154
158
|
instance.second_factor_attempts_count = 2
|
155
|
-
expect(instance.max_login_attempts?).to
|
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
|
164
|
+
expect(instance.max_login_attempts?).to be_truthy
|
161
165
|
instance.second_factor_attempts_count = 4
|
162
|
-
expect(instance.max_login_attempts?).to
|
166
|
+
expect(instance.max_login_attempts?).to be_truthy
|
163
167
|
end
|
164
168
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
@@ -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.
|
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-
|
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:
|
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:
|
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:
|
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:
|
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:
|