authenticate 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|