authenticate 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +27 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +59 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +11 -11
- data/README.md +37 -4
- data/Rakefile +2 -4
- data/app/controllers/authenticate/passwords_controller.rb +3 -3
- data/app/controllers/authenticate/sessions_controller.rb +4 -4
- data/app/controllers/authenticate/users_controller.rb +5 -7
- data/app/mailers/authenticate_mailer.rb +6 -8
- data/authenticate.gemspec +8 -9
- data/lib/authenticate.rb +1 -1
- data/lib/authenticate/callbacks/authenticatable.rb +1 -2
- data/lib/authenticate/callbacks/brute_force.rb +1 -3
- data/lib/authenticate/callbacks/lifetimed.rb +2 -1
- data/lib/authenticate/callbacks/timeoutable.rb +3 -2
- data/lib/authenticate/callbacks/trackable.rb +1 -1
- data/lib/authenticate/configuration.rb +11 -7
- data/lib/authenticate/controller.rb +32 -23
- data/lib/authenticate/crypto/bcrypt.rb +3 -3
- data/lib/authenticate/debug.rb +7 -7
- data/lib/authenticate/engine.rb +4 -2
- data/lib/authenticate/lifecycle.rb +12 -22
- data/lib/authenticate/login_status.rb +4 -3
- data/lib/authenticate/model/brute_force.rb +4 -6
- data/lib/authenticate/model/db_password.rb +5 -14
- data/lib/authenticate/model/email.rb +7 -9
- data/lib/authenticate/model/lifetimed.rb +1 -2
- data/lib/authenticate/model/password_reset.rb +1 -3
- data/lib/authenticate/model/timeoutable.rb +14 -15
- data/lib/authenticate/model/trackable.rb +5 -4
- data/lib/authenticate/model/username.rb +3 -5
- data/lib/authenticate/modules.rb +37 -39
- data/lib/authenticate/session.rb +15 -23
- data/lib/authenticate/token.rb +3 -0
- data/lib/authenticate/user.rb +2 -6
- data/lib/authenticate/version.rb +1 -1
- data/lib/generators/authenticate/controllers/controllers_generator.rb +1 -2
- data/lib/generators/authenticate/helpers.rb +1 -2
- data/lib/generators/authenticate/install/install_generator.rb +31 -32
- data/lib/generators/authenticate/install/templates/authenticate.rb +0 -1
- data/lib/generators/authenticate/routes/routes_generator.rb +1 -2
- data/lib/generators/authenticate/views/USAGE +3 -2
- data/lib/generators/authenticate/views/views_generator.rb +1 -2
- data/spec/controllers/passwords_controller_spec.rb +5 -7
- data/spec/controllers/secured_controller_spec.rb +6 -6
- data/spec/controllers/sessions_controller_spec.rb +2 -2
- data/spec/controllers/users_controller_spec.rb +4 -4
- data/spec/features/brute_force_spec.rb +0 -2
- data/spec/features/max_session_lifetime_spec.rb +0 -1
- data/spec/features/password_reset_spec.rb +10 -19
- data/spec/features/password_update_spec.rb +0 -2
- data/spec/features/sign_out_spec.rb +0 -1
- data/spec/features/sign_up_spec.rb +0 -1
- data/spec/features/timeoutable_spec.rb +0 -1
- data/spec/model/brute_force_spec.rb +2 -3
- data/spec/model/configuration_spec.rb +2 -7
- data/spec/model/db_password_spec.rb +4 -6
- data/spec/model/email_spec.rb +1 -3
- data/spec/model/lifetimed_spec.rb +0 -3
- data/spec/model/modules_spec.rb +22 -0
- data/spec/model/password_reset_spec.rb +3 -10
- data/spec/model/session_spec.rb +4 -5
- data/spec/model/timeoutable_spec.rb +0 -1
- data/spec/model/token_spec.rb +1 -3
- data/spec/model/trackable_spec.rb +1 -2
- data/spec/model/user_spec.rb +0 -1
- data/spec/orm/active_record.rb +1 -1
- data/spec/spec_helper.rb +3 -11
- data/spec/support/controllers/controller_helpers.rb +1 -2
- data/spec/support/features/feature_helpers.rb +2 -4
- metadata +29 -26
data/lib/authenticate/version.rb
CHANGED
@@ -6,7 +6,7 @@ require 'rails/generators/base'
|
|
6
6
|
module Authenticate
|
7
7
|
module Generators
|
8
8
|
class ControllersGenerator < Rails::Generators::Base
|
9
|
-
source_root File.expand_path(
|
9
|
+
source_root File.expand_path('../../../../..', __FILE__)
|
10
10
|
|
11
11
|
def create_controllers
|
12
12
|
directory 'app/controllers'
|
@@ -15,7 +15,6 @@ module Authenticate
|
|
15
15
|
def create_mailers
|
16
16
|
directory 'app/mailers'
|
17
17
|
end
|
18
|
-
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -21,7 +21,7 @@ module Authenticate
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def namespaced?
|
24
|
-
|
24
|
+
!namespace.nil?
|
25
25
|
end
|
26
26
|
|
27
27
|
def model_name
|
@@ -56,7 +56,6 @@ module Authenticate
|
|
56
56
|
@class_path.map!(&:underscore)
|
57
57
|
@file_name = @class_path.pop
|
58
58
|
end
|
59
|
-
|
60
59
|
end
|
61
60
|
end
|
62
61
|
end
|
@@ -9,8 +9,10 @@ module Authenticate
|
|
9
9
|
include Authenticate::Generators::Helpers
|
10
10
|
|
11
11
|
source_root File.expand_path('../templates', __FILE__)
|
12
|
-
|
13
|
-
|
12
|
+
class_option :model,
|
13
|
+
optional: true,
|
14
|
+
type: :string,
|
15
|
+
banner: 'model',
|
14
16
|
desc: "Specify the model class name if you will use anything other than 'User'"
|
15
17
|
|
16
18
|
def initialize(*)
|
@@ -19,7 +21,7 @@ module Authenticate
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def verify
|
22
|
-
if options[:model] && !File.
|
24
|
+
if options[:model] && !File.exist?(model_path)
|
23
25
|
puts "Exiting: the model class you specified, #{options[:model]}, is not found."
|
24
26
|
exit 1
|
25
27
|
end
|
@@ -49,9 +51,9 @@ module Authenticate
|
|
49
51
|
|
50
52
|
def inject_into_application_controller
|
51
53
|
inject_into_class(
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
'app/controllers/application_controller.rb',
|
55
|
+
ApplicationController,
|
56
|
+
" include Authenticate::Controller\n\n"
|
55
57
|
)
|
56
58
|
end
|
57
59
|
|
@@ -59,20 +61,19 @@ module Authenticate
|
|
59
61
|
copy_file 'authenticate.rb', 'config/initializers/authenticate.rb'
|
60
62
|
if options[:model]
|
61
63
|
inject_into_file(
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
'config/initializers/authenticate.rb',
|
65
|
+
" config.user_model = '#{options[:model]}' \n",
|
66
|
+
after: "Authenticate.configure do |config|\n"
|
65
67
|
)
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
69
|
-
|
70
71
|
private
|
71
72
|
|
72
73
|
def create_new_users_migration
|
73
74
|
config = {
|
74
|
-
|
75
|
-
|
75
|
+
new_columns: new_columns,
|
76
|
+
new_indexes: new_indexes
|
76
77
|
}
|
77
78
|
copy_migration 'create_users.rb', config
|
78
79
|
end
|
@@ -80,8 +81,8 @@ module Authenticate
|
|
80
81
|
def create_add_columns_migration
|
81
82
|
if migration_needed?
|
82
83
|
config = {
|
83
|
-
|
84
|
-
|
84
|
+
new_columns: new_columns,
|
85
|
+
new_indexes: new_indexes
|
85
86
|
}
|
86
87
|
copy_migration('add_authenticate_to_users.rb', config)
|
87
88
|
end
|
@@ -90,9 +91,9 @@ module Authenticate
|
|
90
91
|
def copy_migration(migration_name, config = {})
|
91
92
|
unless migration_exists?(migration_name)
|
92
93
|
migration_template(
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
"db/migrate/#{migration_name}",
|
95
|
+
"db/migrate/#{migration_name}",
|
96
|
+
config
|
96
97
|
)
|
97
98
|
end
|
98
99
|
end
|
@@ -103,23 +104,23 @@ module Authenticate
|
|
103
104
|
|
104
105
|
def new_columns
|
105
106
|
@new_columns ||= {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
107
|
+
email: 't.string :email',
|
108
|
+
encrypted_password: 't.string :encrypted_password, limit: 128',
|
109
|
+
session_token: 't.string :session_token, limit: 128',
|
110
|
+
|
111
|
+
# trackable, lifetimed
|
112
|
+
current_sign_in_at: 't.datetime :current_sign_in_at',
|
113
|
+
current_sign_in_ip: 't.string :current_sign_in_ip, limit: 128',
|
114
|
+
last_sign_in_at: 't.datetime :last_sign_in_at',
|
115
|
+
last_sign_in_ip: 't.string :last_sign_in_ip, limit: 128',
|
116
|
+
sign_in_count: 't.integer :sign_in_count'
|
116
117
|
}.reject { |column| existing_users_columns.include?(column.to_s) }
|
117
118
|
end
|
118
119
|
|
119
120
|
def new_indexes
|
120
121
|
@new_indexes ||= {
|
121
|
-
|
122
|
-
|
122
|
+
index_users_on_email: "add_index :#{table_name}, :email",
|
123
|
+
index_users_on_session_token: "add_index :#{table_name}, :session_token"
|
123
124
|
}.reject { |index| existing_users_indexes.include?(index.to_s) }
|
124
125
|
end
|
125
126
|
|
@@ -128,7 +129,7 @@ module Authenticate
|
|
128
129
|
end
|
129
130
|
|
130
131
|
def existing_migrations
|
131
|
-
@existing_migrations ||= Dir.glob(
|
132
|
+
@existing_migrations ||= Dir.glob('db/migrate/*.rb').map do |file|
|
132
133
|
migration_name_without_timestamp(file)
|
133
134
|
end
|
134
135
|
end
|
@@ -155,8 +156,6 @@ module Authenticate
|
|
155
156
|
def self.next_migration_number(dir)
|
156
157
|
ActiveRecord::Generators::Base.next_migration_number(dir)
|
157
158
|
end
|
158
|
-
|
159
|
-
|
160
159
|
end
|
161
160
|
end
|
162
161
|
end
|
@@ -16,7 +16,7 @@ module Authenticate
|
|
16
16
|
inject_into_file(
|
17
17
|
'config/initializers/authenticate.rb',
|
18
18
|
" config.routes = false \n",
|
19
|
-
after: "Authenticate.configure do |config|\n"
|
19
|
+
after: "Authenticate.configure do |config|\n"
|
20
20
|
)
|
21
21
|
end
|
22
22
|
|
@@ -30,7 +30,6 @@ module Authenticate
|
|
30
30
|
def routes_file_path
|
31
31
|
File.expand_path(find_in_source_paths('routes.rb'))
|
32
32
|
end
|
33
|
-
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Description:
|
2
|
-
Override the default authenticate views. This generator will copy all of the
|
3
|
-
base authenticate views into your project.
|
2
|
+
Override the default authenticate views and locale file. This generator will copy all of the
|
3
|
+
base authenticate views and locale file into your project.
|
4
4
|
|
5
5
|
Examples:
|
6
6
|
rails generate authenticate:views
|
@@ -11,3 +11,4 @@ Examples:
|
|
11
11
|
View: app/views/passwords/new.html.erb
|
12
12
|
View: app/views/sessions/new.html.erb
|
13
13
|
View: app/views/users/new.html.erb
|
14
|
+
Locale: config/locales/en.yml
|
@@ -6,7 +6,7 @@ require 'rails/generators/base'
|
|
6
6
|
module Authenticate
|
7
7
|
module Generators
|
8
8
|
class ViewsGenerator < Rails::Generators::Base
|
9
|
-
source_root File.expand_path(
|
9
|
+
source_root File.expand_path('../../../../..', __FILE__)
|
10
10
|
|
11
11
|
def create_views
|
12
12
|
directory 'app/views'
|
@@ -15,7 +15,6 @@ module Authenticate
|
|
15
15
|
def create_locales
|
16
16
|
directory 'config/locales'
|
17
17
|
end
|
18
|
-
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -31,7 +31,7 @@ describe Authenticate::PasswordsController, type: :controller do
|
|
31
31
|
bad_email = 'bunk_email_address@non_existent_domain.com'
|
32
32
|
it 'does not send an email' do
|
33
33
|
ActionMailer::Base.deliveries.clear
|
34
|
-
post :create, password: { email: bad_email}
|
34
|
+
post :create, password: { email: bad_email }
|
35
35
|
expect(ActionMailer::Base.deliveries).to be_empty
|
36
36
|
end
|
37
37
|
it 'always responds with redirect to avoid leaking user information' do
|
@@ -64,7 +64,7 @@ describe Authenticate::PasswordsController, type: :controller do
|
|
64
64
|
user = create(:user, :with_password_reset_token_and_timestamp, password_reset_sent_at: 2.years.ago)
|
65
65
|
get :edit, id: user.id, token: user.password_reset_token
|
66
66
|
expect(response).to be_redirect
|
67
|
-
expect(flash[:notice]).to match
|
67
|
+
expect(flash[:notice]).to match(/password change request has expired/)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
context 'with a blank password_reset_token' do
|
@@ -104,16 +104,14 @@ describe Authenticate::PasswordsController, type: :controller do
|
|
104
104
|
expect(response).to render_template(:edit)
|
105
105
|
end
|
106
106
|
end
|
107
|
-
|
108
107
|
end
|
109
108
|
|
110
109
|
def update_params(user, options = {})
|
111
110
|
new_password = options.fetch(:new_password)
|
112
111
|
{
|
113
|
-
|
114
|
-
|
115
|
-
|
112
|
+
id: user,
|
113
|
+
token: user.password_reset_token,
|
114
|
+
password_reset: { password: new_password }
|
116
115
|
}
|
117
116
|
end
|
118
|
-
|
119
117
|
end
|
@@ -6,18 +6,19 @@ RSpec::Matchers.define :deny_access do
|
|
6
6
|
match do |controller|
|
7
7
|
redirects_to_sign_in?(controller) && sets_flash?(controller)
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
|
+
def redirects_to_sign_in?(controller)
|
10
11
|
expect(controller).to redirect_to(controller.sign_in_url)
|
11
12
|
end
|
12
|
-
|
13
|
-
|
13
|
+
|
14
|
+
def sets_flash?(controller)
|
15
|
+
controller.flash[:notice].match(/sign in to continue/)
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
# A dummy 'secured' controller to test
|
18
20
|
class SecuredAppsController < ActionController::Base
|
19
21
|
include Authenticate::Controller
|
20
|
-
|
21
22
|
before_action :require_authentication, only: :show
|
22
23
|
|
23
24
|
def new
|
@@ -29,7 +30,6 @@ class SecuredAppsController < ActionController::Base
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
33
|
describe SecuredAppsController, type: :controller do
|
34
34
|
before do
|
35
35
|
Rails.application.routes.draw do
|
@@ -37,7 +37,7 @@ describe Authenticate::SessionsController, type: :controller do
|
|
37
37
|
context 'with good password' do
|
38
38
|
before do
|
39
39
|
@user = create(:user)
|
40
|
-
post :create, session:{ email: @user.email, password: @user.password }
|
40
|
+
post :create, session: { email: @user.email, password: @user.password }
|
41
41
|
end
|
42
42
|
it { is_expected.to respond_with 302 }
|
43
43
|
|
@@ -63,6 +63,7 @@ describe Authenticate::SessionsController, type: :controller do
|
|
63
63
|
|
64
64
|
it { is_expected.to redirect_to sign_in_url }
|
65
65
|
end
|
66
|
+
|
66
67
|
context 'with a session cookie' do
|
67
68
|
before do
|
68
69
|
@user = create(:user, session_token: 'old-session-token')
|
@@ -82,5 +83,4 @@ describe Authenticate::SessionsController, type: :controller do
|
|
82
83
|
end
|
83
84
|
end
|
84
85
|
end
|
85
|
-
|
86
86
|
end
|
@@ -26,6 +26,7 @@ describe Authenticate::UsersController, type: :controller do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
29
30
|
describe 'post to #create' do
|
30
31
|
context 'not signed in' do
|
31
32
|
context 'with valid attributes' do
|
@@ -33,7 +34,7 @@ describe Authenticate::UsersController, type: :controller do
|
|
33
34
|
subject { post :create, user: user_attributes }
|
34
35
|
|
35
36
|
it 'creates user' do
|
36
|
-
expect{ subject }.to change{ User.count }.by(1)
|
37
|
+
expect { subject }.to change { User.count }.by(1)
|
37
38
|
end
|
38
39
|
|
39
40
|
it 'assigned user' do
|
@@ -55,7 +56,7 @@ describe Authenticate::UsersController, type: :controller do
|
|
55
56
|
subject { post :create, user: user_attributes }
|
56
57
|
|
57
58
|
it 'creates user' do
|
58
|
-
expect{ subject }.to change{ User.count }.by(1)
|
59
|
+
expect { subject }.to change { User.count }.by(1)
|
59
60
|
end
|
60
61
|
|
61
62
|
it 'assigned user' do
|
@@ -68,8 +69,8 @@ describe Authenticate::UsersController, type: :controller do
|
|
68
69
|
expect(response).to redirect_to '/url_in_the_session'
|
69
70
|
end
|
70
71
|
end
|
71
|
-
|
72
72
|
end
|
73
|
+
|
73
74
|
context 'signed in' do
|
74
75
|
it 'redirects to redirect_url' do
|
75
76
|
sign_in
|
@@ -78,5 +79,4 @@ describe Authenticate::UsersController, type: :controller do
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
end
|
81
|
-
|
82
82
|
end
|
@@ -36,8 +36,7 @@ feature 'visitor requests password reset' do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
def request_password_reset_for email
|
39
|
+
def request_password_reset_for(email)
|
41
40
|
visit new_password_path
|
42
41
|
fill_in 'password_email', with: email
|
43
42
|
click_button 'Reset password'
|
@@ -47,20 +46,18 @@ def expect_password_change_request_success_message
|
|
47
46
|
expect(page).to have_content I18n.t('passwords.create.description')
|
48
47
|
end
|
49
48
|
|
50
|
-
def expect_user_to_have_password_reset_attributes
|
49
|
+
def expect_user_to_have_password_reset_attributes(user)
|
51
50
|
user.reload
|
52
51
|
expect(user.password_reset_token).not_to be_blank
|
53
52
|
expect(user.password_reset_sent_at).not_to be_blank
|
54
53
|
end
|
55
54
|
|
56
|
-
def expect_password_reset_email_for
|
57
|
-
recipient = user.email
|
58
|
-
token = user.password_reset_token
|
55
|
+
def expect_password_reset_email_for(user)
|
59
56
|
expect(ActionMailer::Base.deliveries).not_to be_empty
|
60
57
|
ActionMailer::Base.deliveries.any? do |email|
|
61
|
-
email.to == [
|
62
|
-
|
63
|
-
|
58
|
+
email.to == [user.email] &&
|
59
|
+
email.html_part.body =~ /#{user.password_reset_token}/ &&
|
60
|
+
email.text_part.body =~ /#{user.password_reset_token}/
|
64
61
|
end
|
65
62
|
end
|
66
63
|
|
@@ -68,15 +65,11 @@ def expect_mailer_to_have_no_deliveries
|
|
68
65
|
expect(ActionMailer::Base.deliveries).to be_empty
|
69
66
|
end
|
70
67
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
68
|
feature 'visitor sets new password' do
|
76
69
|
scenario 'requests password change' do
|
77
70
|
user = given_user_with_password_reset_token
|
78
71
|
visit_password_update_page_for user
|
79
|
-
|
72
|
+
request_password_change
|
80
73
|
expect_password_is_changed_for user
|
81
74
|
expect_redirect_to_root
|
82
75
|
end
|
@@ -88,7 +81,6 @@ feature 'visitor sets new password' do
|
|
88
81
|
end
|
89
82
|
end
|
90
83
|
|
91
|
-
|
92
84
|
def given_user_with_fake_password_reset_token
|
93
85
|
user = create :user
|
94
86
|
user.password_reset_token = 'big_fake_token'
|
@@ -99,16 +91,16 @@ def given_user_with_password_reset_token
|
|
99
91
|
create :user, :with_password_reset_token_and_timestamp
|
100
92
|
end
|
101
93
|
|
102
|
-
def visit_password_update_page_for
|
94
|
+
def visit_password_update_page_for(user)
|
103
95
|
visit edit_users_password_path(user.id, token: user.password_reset_token)
|
104
96
|
end
|
105
97
|
|
106
|
-
def
|
98
|
+
def request_password_change
|
107
99
|
fill_in 'password_reset_password', with: 'new_dumb_password'
|
108
100
|
click_button 'Save this password'
|
109
101
|
end
|
110
102
|
|
111
|
-
def expect_password_is_changed_for
|
103
|
+
def expect_password_is_changed_for(user)
|
112
104
|
old_encrypted_password = user.encrypted_password
|
113
105
|
expect(user.reload.encrypted_password).to_not eq old_encrypted_password
|
114
106
|
end
|
@@ -120,4 +112,3 @@ end
|
|
120
112
|
def expect_failure_flash
|
121
113
|
expect(page).to have_content 'Please double check the URL or try submitting the form again.'
|
122
114
|
end
|
123
|
-
|