authenticate 0.2.3 → 0.3.0
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/.gitignore +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +21 -6
- data/app/controllers/authenticate/passwords_controller.rb +1 -1
- data/authenticate.gemspec +7 -3
- data/config/locales/authenticate.en.yml +1 -1
- data/gemfiles/rails42.gemfile +6 -1
- data/lib/authenticate/callbacks/brute_force.rb +3 -4
- data/lib/authenticate/configuration.rb +2 -2
- data/lib/authenticate/controller.rb +1 -2
- data/lib/authenticate/model/brute_force.rb +2 -2
- data/lib/authenticate/model/db_password.rb +2 -3
- data/lib/authenticate/model/email.rb +3 -6
- data/lib/authenticate/model/lifetimed.rb +1 -1
- data/lib/authenticate/model/password_reset.rb +1 -1
- data/lib/authenticate/model/timeoutable.rb +2 -2
- data/lib/authenticate/model/trackable.rb +1 -1
- data/lib/authenticate/model/username.rb +1 -1
- data/lib/authenticate/session.rb +0 -4
- data/lib/authenticate/user.rb +12 -0
- data/lib/authenticate/version.rb +1 -1
- data/spec/controllers/passwords_controller_spec.rb +119 -0
- data/spec/controllers/secured_controller_spec.rb +70 -0
- data/spec/controllers/sessions_controller_spec.rb +86 -0
- data/spec/controllers/users_controller_spec.rb +82 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/welcome_controller.rb +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/welcome/index.html.erb +4 -0
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/config/environments/production.rb +12 -0
- data/spec/dummy/config/initializers/authenticate.rb +4 -11
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/factories/users.rb +2 -4
- data/spec/features/brute_force_spec.rb +49 -0
- data/spec/features/max_session_lifetime_spec.rb +30 -0
- data/spec/features/password_reset_spec.rb +69 -0
- data/spec/features/password_update_spec.rb +41 -0
- data/spec/features/sign_in_spec.rb +29 -0
- data/spec/features/sign_out_spec.rb +22 -0
- data/spec/features/sign_up_spec.rb +42 -0
- data/spec/features/timeoutable_spec.rb +30 -0
- data/spec/model/brute_force_spec.rb +26 -29
- data/spec/model/configuration_spec.rb +61 -0
- data/spec/model/db_password_spec.rb +8 -9
- data/spec/model/email_spec.rb +0 -1
- data/spec/model/lifetimed_spec.rb +6 -18
- data/spec/model/password_reset_spec.rb +2 -9
- data/spec/model/session_spec.rb +16 -23
- data/spec/model/timeoutable_spec.rb +8 -7
- data/spec/model/trackable_spec.rb +0 -1
- data/spec/model/user_spec.rb +1 -2
- data/spec/spec_helper.rb +33 -131
- data/spec/support/controllers/controller_helpers.rb +24 -0
- data/spec/support/features/feature_helpers.rb +36 -0
- metadata +80 -8
- data/spec/configuration_spec.rb +0 -60
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor requests password reset' do
|
5
|
+
before(:each) do
|
6
|
+
ActionMailer::Base.deliveries.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
scenario 'navigates to Forgot Password page' do
|
10
|
+
visit sign_in_path
|
11
|
+
click_link 'Forgot Password'
|
12
|
+
expect(current_path).to eq new_password_path
|
13
|
+
end
|
14
|
+
|
15
|
+
scenario 'uses valid email' do
|
16
|
+
user = create(:user)
|
17
|
+
request_password_reset_for user.email
|
18
|
+
|
19
|
+
expect_password_change_request_success_message
|
20
|
+
expect_user_to_have_password_reset_attributes user
|
21
|
+
expect_password_reset_email_for user
|
22
|
+
end
|
23
|
+
|
24
|
+
scenario 'with a non-user-account email' do
|
25
|
+
request_password_reset_for 'fake.email@example.com'
|
26
|
+
|
27
|
+
expect_password_change_request_success_message
|
28
|
+
expect_mailer_to_have_no_deliveries
|
29
|
+
end
|
30
|
+
|
31
|
+
scenario 'with invalid email' do
|
32
|
+
request_password_reset_for 'not_an_email_address'
|
33
|
+
|
34
|
+
expect_password_change_request_success_message
|
35
|
+
expect_mailer_to_have_no_deliveries
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def request_password_reset_for email
|
41
|
+
visit new_password_path
|
42
|
+
fill_in 'password_email', with: email
|
43
|
+
click_button 'Reset password'
|
44
|
+
end
|
45
|
+
|
46
|
+
def expect_password_change_request_success_message
|
47
|
+
expect(page).to have_content I18n.t('passwords.create.description')
|
48
|
+
end
|
49
|
+
|
50
|
+
def expect_user_to_have_password_reset_attributes user
|
51
|
+
user.reload
|
52
|
+
expect(user.password_reset_token).not_to be_blank
|
53
|
+
expect(user.password_reset_sent_at).not_to be_blank
|
54
|
+
end
|
55
|
+
|
56
|
+
def expect_password_reset_email_for user
|
57
|
+
recipient = user.email
|
58
|
+
token = user.password_reset_token
|
59
|
+
expect(ActionMailer::Base.deliveries).not_to be_empty
|
60
|
+
ActionMailer::Base.deliveries.any? do |email|
|
61
|
+
email.to == [recipient] &&
|
62
|
+
email.html_part.body =~ /#{token}/ &&
|
63
|
+
email.text_part.body =~ /#{token}/
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def expect_mailer_to_have_no_deliveries
|
68
|
+
expect(ActionMailer::Base.deliveries).to be_empty
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor updates password' do
|
5
|
+
before(:each) do
|
6
|
+
@user = create(:user, :with_password_reset_token_and_timestamp)
|
7
|
+
end
|
8
|
+
|
9
|
+
scenario 'with a valid password' do
|
10
|
+
update_password @user, 'newpassword'
|
11
|
+
|
12
|
+
expect_user_to_be_signed_in
|
13
|
+
end
|
14
|
+
|
15
|
+
scenario 'with a blank password' do
|
16
|
+
update_password @user, ''
|
17
|
+
|
18
|
+
expect(page).to have_content I18n.t('flashes.failure_after_update')
|
19
|
+
expect_user_to_be_signed_out
|
20
|
+
end
|
21
|
+
|
22
|
+
scenario 'signs in with new password' do
|
23
|
+
update_password @user, 'newpassword'
|
24
|
+
|
25
|
+
sign_out
|
26
|
+
sign_in_with @user.email, 'newpassword'
|
27
|
+
expect_user_to_be_signed_in
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def update_password(user, password)
|
33
|
+
visit_password_reset_page_for user
|
34
|
+
fill_in 'password_reset_password', with: password
|
35
|
+
click_button 'Save this password'
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_password_reset_page_for(user)
|
39
|
+
visit edit_users_password_path(id: user, token: user.password_reset_token)
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor signs in' do
|
5
|
+
scenario 'with valid email and password' do
|
6
|
+
user = create(:user)
|
7
|
+
sign_in_with user.email, user.password
|
8
|
+
expect_user_to_be_signed_in
|
9
|
+
end
|
10
|
+
|
11
|
+
scenario 'with valid mixed-case email and password' do
|
12
|
+
user = create(:user, email: 'test.user@example.com')
|
13
|
+
sign_in_with 'Test.USER@example.com', user.password
|
14
|
+
expect_user_to_be_signed_in
|
15
|
+
end
|
16
|
+
|
17
|
+
scenario 'with invalid password' do
|
18
|
+
user = create(:user)
|
19
|
+
sign_in_with user.email, 'invalid password'
|
20
|
+
expect_page_to_display_sign_in_error
|
21
|
+
expect_user_to_be_signed_out
|
22
|
+
end
|
23
|
+
|
24
|
+
scenario 'with invalid email' do
|
25
|
+
sign_in_with 'unknown@example.com', 'password'
|
26
|
+
expect_page_to_display_sign_in_error
|
27
|
+
expect_user_to_be_signed_out
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor signs out' do
|
5
|
+
before do
|
6
|
+
@user = create(:user)
|
7
|
+
end
|
8
|
+
|
9
|
+
scenario 'sign in and sign out' do
|
10
|
+
sign_in_with(@user.email, @user.password)
|
11
|
+
sign_out
|
12
|
+
expect_user_to_be_signed_out
|
13
|
+
end
|
14
|
+
|
15
|
+
scenario 'sign out and sign out again' do
|
16
|
+
sign_in_with(@user.email, @user.password)
|
17
|
+
visit sign_out_path
|
18
|
+
visit sign_out_path
|
19
|
+
expect_user_to_be_signed_out
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor signs up' do
|
5
|
+
scenario 'navigates to sign up page' do
|
6
|
+
visit sign_in_path
|
7
|
+
click_link 'Sign Up'
|
8
|
+
expect_sign_up_page
|
9
|
+
end
|
10
|
+
|
11
|
+
scenario 'signs up with valid email and password' do
|
12
|
+
sign_up_with 'valid@example.com', 'password'
|
13
|
+
expect_user_to_be_signed_in
|
14
|
+
end
|
15
|
+
|
16
|
+
scenario 'signs up with invalid email' do
|
17
|
+
sign_up_with 'bad_email', 'password'
|
18
|
+
expect_user_to_be_signed_out
|
19
|
+
end
|
20
|
+
|
21
|
+
scenario 'signs up with invalid short password' do
|
22
|
+
sign_up_with 'bad_email', '111'
|
23
|
+
expect_user_to_be_signed_out
|
24
|
+
end
|
25
|
+
|
26
|
+
scenario 'signs up with blank password' do
|
27
|
+
sign_up_with 'bad_email', ''
|
28
|
+
expect_user_to_be_signed_out
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def expect_sign_up_page
|
34
|
+
expect(current_path).to eq sign_up_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def sign_up_with(email, password)
|
38
|
+
visit sign_up_path
|
39
|
+
fill_in 'user_email', with: email
|
40
|
+
fill_in 'user_password', with: password
|
41
|
+
click_button 'Sign up'
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/features/feature_helpers'
|
3
|
+
|
4
|
+
feature 'visitor session time' do
|
5
|
+
before do
|
6
|
+
@user = create(:user)
|
7
|
+
end
|
8
|
+
|
9
|
+
scenario 'visitor logs in, subsequent click within timeout' do
|
10
|
+
sign_in_with @user.email, @user.password
|
11
|
+
expect_user_to_be_signed_in
|
12
|
+
|
13
|
+
Timecop.travel 10.minutes do
|
14
|
+
visit root_path
|
15
|
+
expect_user_to_be_signed_in
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
scenario 'visitor logs in, subsequent click after session times out' do
|
20
|
+
sign_in_with @user.email, @user.password
|
21
|
+
expect_user_to_be_signed_in
|
22
|
+
|
23
|
+
Timecop.travel 21.minutes do
|
24
|
+
visit root_path
|
25
|
+
expect(current_path).to eq sign_in_path
|
26
|
+
expect_user_to_be_signed_out
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -3,61 +3,58 @@ require 'authenticate/model/brute_force'
|
|
3
3
|
|
4
4
|
|
5
5
|
describe Authenticate::Model::BruteForce do
|
6
|
-
before
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
before(:each) do
|
7
|
+
@user = create(:user)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'responds to locked?' do
|
11
|
+
expect(@user).to respond_to :locked?
|
12
|
+
end
|
12
13
|
|
13
14
|
it 'knows when it is locked' do
|
14
|
-
user
|
15
|
-
|
16
|
-
user.
|
17
|
-
expect(user.locked?).to be_truthy
|
15
|
+
expect(@user.locked?).to be_falsey
|
16
|
+
@user.lock!
|
17
|
+
expect(@user.locked?).to be_truthy
|
18
18
|
end
|
19
19
|
|
20
20
|
context '#register_failed_login!' do
|
21
21
|
it 'locks when failed login count reaches max' do
|
22
|
-
user
|
23
|
-
user.register_failed_login!
|
24
|
-
user.register_failed_login!
|
25
|
-
expect(user.locked?).to be_truthy
|
22
|
+
@user.register_failed_login!
|
23
|
+
@user.register_failed_login!
|
24
|
+
@user.register_failed_login!
|
25
|
+
expect(@user.locked?).to be_truthy
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'sets lockout period' do
|
29
|
-
user
|
30
|
-
user.register_failed_login!
|
31
|
-
user.register_failed_login!
|
32
|
-
expect(user.lock_expires_at).to_not be_nil
|
29
|
+
@user.register_failed_login!
|
30
|
+
@user.register_failed_login!
|
31
|
+
@user.register_failed_login!
|
32
|
+
expect(@user.lock_expires_at).to_not be_nil
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
context '#lock!' do
|
37
37
|
it 'before lock, locked_expires_at is nil' do
|
38
|
-
user
|
39
|
-
expect(user.lock_expires_at).to be_nil
|
38
|
+
expect(@user.lock_expires_at).to be_nil
|
40
39
|
end
|
41
40
|
|
42
41
|
it 'sets locked_expires_at' do
|
43
|
-
user
|
44
|
-
user.
|
45
|
-
expect(user.lock_expires_at).
|
46
|
-
expect(user.lock_expires_at).to be_utc
|
42
|
+
@user.lock!
|
43
|
+
expect(@user.lock_expires_at).to_not be_nil
|
44
|
+
expect(@user.lock_expires_at).to be_utc
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
50
48
|
context '#unlock!' do
|
51
|
-
let(:user) { User.new }
|
52
49
|
before(:each) {
|
53
|
-
user.lock!
|
54
|
-
user.unlock!
|
50
|
+
@user.lock!
|
51
|
+
@user.unlock!
|
55
52
|
}
|
56
53
|
it 'zeros failed_logins_count' do
|
57
|
-
expect(user.failed_logins_count).to be(0)
|
54
|
+
expect(@user.failed_logins_count).to be(0)
|
58
55
|
end
|
59
56
|
it 'nils lock_expires_at' do
|
60
|
-
expect(user.lock_expires_at).to be_nil
|
57
|
+
expect(@user.lock_expires_at).to be_nil
|
61
58
|
end
|
62
59
|
end
|
63
60
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'authenticate/configuration'
|
3
|
+
|
4
|
+
describe Authenticate::Configuration do
|
5
|
+
|
6
|
+
context 'user model' do
|
7
|
+
module Gug
|
8
|
+
class Profile
|
9
|
+
extend ActiveModel::Naming
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@conf = Authenticate::Configuration.new
|
15
|
+
@conf.user_model = 'Gug::Profile'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'gets a class for a user model' do
|
19
|
+
expect(@conf.user_model_class).to be(Gug::Profile)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'get a route key for a user model' do
|
23
|
+
expect(@conf.user_model_route_key).to eq('gug_profiles')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'get a param key for a user model' do
|
27
|
+
expect(@conf.user_model_param_key).to eq('gug_profile')
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#authentication_strategy' do
|
31
|
+
context 'with no strategy set' do
|
32
|
+
it 'defaults to email' do
|
33
|
+
expect(@conf.authentication_strategy).to eq :email
|
34
|
+
end
|
35
|
+
it 'includes email in modules' do
|
36
|
+
expect(@conf.modules).to include :email
|
37
|
+
end
|
38
|
+
it 'does not include username in modules' do
|
39
|
+
expect(@conf.modules).to_not include :username
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with strategy set to username' do
|
44
|
+
before do
|
45
|
+
@conf.authentication_strategy = :username
|
46
|
+
end
|
47
|
+
it 'includes username in modules' do
|
48
|
+
expect(@conf.modules).to include :username
|
49
|
+
end
|
50
|
+
it 'does not include email in modules' do
|
51
|
+
expect(@conf.modules).to_not include :email
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -4,7 +4,6 @@ require 'authenticate/model/db_password'
|
|
4
4
|
|
5
5
|
describe Authenticate::Model::DbPassword do
|
6
6
|
describe 'Passwords' do
|
7
|
-
|
8
7
|
context '#password_match?' do
|
9
8
|
subject { create(:user, password: 'password') }
|
10
9
|
|
@@ -26,12 +25,6 @@ describe Authenticate::Model::DbPassword do
|
|
26
25
|
end
|
27
26
|
|
28
27
|
describe 'Validations' do
|
29
|
-
before(:all) {
|
30
|
-
Authenticate.configure do |config|
|
31
|
-
config.password_length = 8..128
|
32
|
-
end
|
33
|
-
}
|
34
|
-
|
35
28
|
context 'on a new user' do
|
36
29
|
it 'should not be valid without a password' do
|
37
30
|
user = build(:user, :without_password)
|
@@ -39,12 +32,16 @@ describe Authenticate::Model::DbPassword do
|
|
39
32
|
end
|
40
33
|
|
41
34
|
it 'should be not be valid with a short password' do
|
42
|
-
user = build(:user
|
35
|
+
user = build(:user)
|
36
|
+
user.password = 'short'
|
37
|
+
user.password_changing = true
|
43
38
|
expect(user).to_not be_valid
|
44
39
|
end
|
45
40
|
|
46
41
|
it 'is valid with a long password' do
|
47
|
-
user = build(:user
|
42
|
+
user = build(:user)
|
43
|
+
user.password = 'thisisalongpassword'
|
44
|
+
user.password_changing = true
|
48
45
|
expect(user).to be_valid
|
49
46
|
end
|
50
47
|
end
|
@@ -56,11 +53,13 @@ describe Authenticate::Model::DbPassword do
|
|
56
53
|
|
57
54
|
it 'should not be valid with an empty password' do
|
58
55
|
subject.password = ''
|
56
|
+
subject.password_changing = true
|
59
57
|
expect(subject).to_not be_valid
|
60
58
|
end
|
61
59
|
|
62
60
|
it 'should be valid with a new (valid) password' do
|
63
61
|
subject.password = 'new password'
|
62
|
+
subject.password_changing = true
|
64
63
|
expect(subject).to be_valid
|
65
64
|
end
|
66
65
|
end
|