two_factor_authentication 1.1.1 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +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:
|