authenticate 0.3.1 → 0.3.2
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/.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
|
-
|