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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile +1 -0
  5. data/Gemfile.lock +21 -6
  6. data/app/controllers/authenticate/passwords_controller.rb +1 -1
  7. data/authenticate.gemspec +7 -3
  8. data/config/locales/authenticate.en.yml +1 -1
  9. data/gemfiles/rails42.gemfile +6 -1
  10. data/lib/authenticate/callbacks/brute_force.rb +3 -4
  11. data/lib/authenticate/configuration.rb +2 -2
  12. data/lib/authenticate/controller.rb +1 -2
  13. data/lib/authenticate/model/brute_force.rb +2 -2
  14. data/lib/authenticate/model/db_password.rb +2 -3
  15. data/lib/authenticate/model/email.rb +3 -6
  16. data/lib/authenticate/model/lifetimed.rb +1 -1
  17. data/lib/authenticate/model/password_reset.rb +1 -1
  18. data/lib/authenticate/model/timeoutable.rb +2 -2
  19. data/lib/authenticate/model/trackable.rb +1 -1
  20. data/lib/authenticate/model/username.rb +1 -1
  21. data/lib/authenticate/session.rb +0 -4
  22. data/lib/authenticate/user.rb +12 -0
  23. data/lib/authenticate/version.rb +1 -1
  24. data/spec/controllers/passwords_controller_spec.rb +119 -0
  25. data/spec/controllers/secured_controller_spec.rb +70 -0
  26. data/spec/controllers/sessions_controller_spec.rb +86 -0
  27. data/spec/controllers/users_controller_spec.rb +82 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  29. data/spec/dummy/app/controllers/welcome_controller.rb +4 -0
  30. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  31. data/spec/dummy/app/views/welcome/index.html.erb +4 -0
  32. data/spec/dummy/config/application.rb +2 -0
  33. data/spec/dummy/config/environments/production.rb +12 -0
  34. data/spec/dummy/config/initializers/authenticate.rb +4 -11
  35. data/spec/dummy/config/routes.rb +3 -0
  36. data/spec/dummy/db/test.sqlite3 +0 -0
  37. data/spec/factories/users.rb +2 -4
  38. data/spec/features/brute_force_spec.rb +49 -0
  39. data/spec/features/max_session_lifetime_spec.rb +30 -0
  40. data/spec/features/password_reset_spec.rb +69 -0
  41. data/spec/features/password_update_spec.rb +41 -0
  42. data/spec/features/sign_in_spec.rb +29 -0
  43. data/spec/features/sign_out_spec.rb +22 -0
  44. data/spec/features/sign_up_spec.rb +42 -0
  45. data/spec/features/timeoutable_spec.rb +30 -0
  46. data/spec/model/brute_force_spec.rb +26 -29
  47. data/spec/model/configuration_spec.rb +61 -0
  48. data/spec/model/db_password_spec.rb +8 -9
  49. data/spec/model/email_spec.rb +0 -1
  50. data/spec/model/lifetimed_spec.rb +6 -18
  51. data/spec/model/password_reset_spec.rb +2 -9
  52. data/spec/model/session_spec.rb +16 -23
  53. data/spec/model/timeoutable_spec.rb +8 -7
  54. data/spec/model/trackable_spec.rb +0 -1
  55. data/spec/model/user_spec.rb +1 -2
  56. data/spec/spec_helper.rb +33 -131
  57. data/spec/support/controllers/controller_helpers.rb +24 -0
  58. data/spec/support/features/feature_helpers.rb +36 -0
  59. metadata +80 -8
  60. data/spec/configuration_spec.rb +0 -60
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ require 'support/controllers/controller_helpers'
3
+
4
+ # Matcher that asserts user was denied access.
5
+ RSpec::Matchers.define :deny_access do
6
+ match do |controller|
7
+ redirects_to_sign_in?(controller) && sets_flash?(controller)
8
+ end
9
+ def redirects_to_sign_in? controller
10
+ expect(controller).to redirect_to(controller.sign_in_url)
11
+ end
12
+ def sets_flash? controller
13
+ controller.flash[:notice].match /sign in to continue/
14
+ end
15
+ end
16
+
17
+
18
+ class SecuredAppsController < ActionController::Base
19
+ include Authenticate::Controller
20
+
21
+ before_action :require_authentication, only: :show
22
+
23
+ def new
24
+ head :ok
25
+ end
26
+
27
+ def show
28
+ head :ok
29
+ end
30
+ end
31
+
32
+
33
+ describe SecuredAppsController, type: :controller do
34
+ before do
35
+ Rails.application.routes.draw do
36
+ resource :secured_app, only: [:new, :show]
37
+ get '/sign_in' => 'authenticate/sessions#new', as: 'sign_in'
38
+ end
39
+ end
40
+
41
+ after do
42
+ Rails.application.reload_routes!
43
+ end
44
+
45
+ context 'with authenticated user' do
46
+ before { sign_in }
47
+
48
+ it 'allows access to new' do
49
+ get :new
50
+ expect(subject).to_not deny_access
51
+ end
52
+
53
+ it 'allows access to show' do
54
+ get :show
55
+ expect(subject).to_not deny_access
56
+ end
57
+ end
58
+
59
+ context 'with an unauthenticated visitor' do
60
+ it 'allows access to new' do
61
+ get :new
62
+ expect(subject).to_not deny_access
63
+ end
64
+
65
+ it 'denies access to show' do
66
+ get :show
67
+ expect(subject).to deny_access
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'support/controllers/controller_helpers'
3
+
4
+ describe Authenticate::SessionsController, type: :controller do
5
+ it { is_expected.to be_a Authenticate::Controller }
6
+
7
+ describe 'get to #new' do
8
+ context 'when user not signed in' do
9
+ before do
10
+ get :new
11
+ end
12
+ it { is_expected.to respond_with 200 }
13
+ it { is_expected.to render_template :new }
14
+ it { is_expected.not_to set_flash }
15
+ end
16
+
17
+ context 'when user is signed in' do
18
+ before do
19
+ sign_in
20
+ get :new
21
+ end
22
+
23
+ it { is_expected.not_to set_flash }
24
+ it { is_expected.to redirect_to(Authenticate.configuration.redirect_url) }
25
+ end
26
+ end
27
+
28
+ describe 'post to #create' do
29
+ context 'without password' do
30
+ it 'renders page with error' do
31
+ user = create(:user)
32
+ post :create, session: { email: user.email }
33
+ expect(response).to render_template :new
34
+ expect(flash[:notice]).to match(/Invalid id or password/)
35
+ end
36
+ end
37
+ context 'with good password' do
38
+ before do
39
+ @user = create(:user)
40
+ post :create, session:{ email: @user.email, password: @user.password }
41
+ end
42
+ it { is_expected.to respond_with 302 }
43
+
44
+ it { is_expected.to redirect_to Authenticate.configuration.redirect_url }
45
+
46
+ it 'sets user session token' do
47
+ @user.reload
48
+ expect(@user.session_token).to_not be_nil
49
+ end
50
+
51
+ it 'sets user session' do
52
+ expect(controller.current_user).to eq(@user)
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'delete to #destroy' do
58
+ context 'with a signed out user' do
59
+ before do
60
+ sign_out
61
+ get :destroy
62
+ end
63
+
64
+ it { is_expected.to redirect_to sign_in_url }
65
+ end
66
+ context 'with a session cookie' do
67
+ before do
68
+ @user = create(:user, session_token: 'old-session-token')
69
+ @request.cookies['authenticate_session_token'] = 'old-session-token'
70
+ get :destroy
71
+ end
72
+
73
+ it { is_expected.to redirect_to sign_in_url }
74
+
75
+ it 'reset the session token' do
76
+ @user.reload
77
+ expect(@user.session_token).to_not eq('old-session-token')
78
+ end
79
+
80
+ it 'unset the current user' do
81
+ expect(controller.current_user).to be_nil
82
+ end
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'support/controllers/controller_helpers'
3
+
4
+ describe Authenticate::UsersController, type: :controller do
5
+ it { is_expected.to be_a Authenticate::Controller }
6
+
7
+ describe 'get to #new' do
8
+ context 'not signed in' do
9
+ it 'renders form' do
10
+ get :new
11
+ expect(response).to be_success
12
+ expect(response).to render_template(:new)
13
+ end
14
+ it 'defaults email field to the value provided in the query string' do
15
+ get :new, user: { email: 'dude@example.com' }
16
+ expect(assigns(:user).email).to eq 'dude@example.com'
17
+ expect(response).to be_success
18
+ expect(response).to render_template(:new)
19
+ end
20
+ end
21
+ context 'signed in' do
22
+ it 'redirects user to the redirect_url' do
23
+ sign_in
24
+ get :new
25
+ expect(response).to redirect_to Authenticate.configuration.redirect_url
26
+ end
27
+ end
28
+ end
29
+ describe 'post to #create' do
30
+ context 'not signed in' do
31
+ context 'with valid attributes' do
32
+ let(:user_attributes) { attributes_for(:user) }
33
+ subject { post :create, user: user_attributes }
34
+
35
+ it 'creates user' do
36
+ expect{ subject }.to change{ User.count }.by(1)
37
+ end
38
+
39
+ it 'assigned user' do
40
+ subject
41
+ expect(assigns(:user)).to be_present
42
+ end
43
+
44
+ it 'redirects to the redirect_url' do
45
+ subject
46
+ expect(response).to redirect_to Authenticate.configuration.redirect_url
47
+ end
48
+ end
49
+
50
+ context 'with valid attributes and a session return cookie' do
51
+ before do
52
+ @request.cookies[:authenticate_return_to] = '/url_in_the_session'
53
+ end
54
+ let(:user_attributes) { attributes_for(:user) }
55
+ subject { post :create, user: user_attributes }
56
+
57
+ it 'creates user' do
58
+ expect{ subject }.to change{ User.count }.by(1)
59
+ end
60
+
61
+ it 'assigned user' do
62
+ subject
63
+ expect(assigns(:user)).to be_present
64
+ end
65
+
66
+ it 'redirects to the redirect_url' do
67
+ subject
68
+ expect(response).to redirect_to '/url_in_the_session'
69
+ end
70
+ end
71
+
72
+ end
73
+ context 'signed in' do
74
+ it 'redirects to redirect_url' do
75
+ sign_in
76
+ post :create, user: {}
77
+ expect(response).to redirect_to Authenticate.configuration.redirect_url
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -1,5 +1,7 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  include Authenticate::Controller
3
+ before_action :require_authentication
4
+
3
5
  # Prevent CSRF attacks by raising an exception.
4
6
  # For APIs, you may want to use :null_session instead.
5
7
  protect_from_forgery with: :exception
@@ -0,0 +1,4 @@
1
+ class WelcomeController < ApplicationController
2
+ def index
3
+ end
4
+ end
@@ -8,6 +8,20 @@
8
8
  </head>
9
9
  <body>
10
10
 
11
+ <div id="header">
12
+ <% if authenticated? -%>
13
+ <%= link_to t(".sign_out"), sign_out_path %>
14
+ <% else -%>
15
+ <%= link_to t(".sign_in"), sign_in_path %>
16
+ <% end -%>
17
+ </div>
18
+
19
+ <div id="flash">
20
+ <% flash.each do |key, value| -%>
21
+ <div id="flash_<%= key %>"><%=h value %></div>
22
+ <% end %>
23
+ </div>
24
+
11
25
  <%= yield %>
12
26
 
13
27
  </body>
@@ -0,0 +1,4 @@
1
+ <h1>Welcome#index</h1>
2
+ <p>Find me in app/views/welcome/index.html.erb</p>
3
+
4
+ <%= link_to "Sign out", sign_out_path %>
@@ -21,6 +21,8 @@ module Dummy
21
21
 
22
22
  # Do not swallow errors in after_commit/after_rollback callbacks.
23
23
  config.active_record.raise_in_transactional_callbacks = true
24
+
24
25
  end
25
26
  end
26
27
 
28
+ Rails.application.routes.default_url_options[:host] = 'localhost:3000'
@@ -64,6 +64,17 @@ Rails.application.configure do
64
64
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65
65
  # config.action_mailer.raise_delivery_errors = false
66
66
 
67
+
68
+
69
+
70
+ # Tell Action Mailer not to deliver emails to the real world.
71
+ # The :test delivery method accumulates sent emails in the
72
+ # ActionMailer::Base.deliveries array.
73
+ config.action_mailer.delivery_method = :test
74
+
75
+
76
+
77
+
67
78
  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68
79
  # the I18n.default_locale when a translation cannot be found).
69
80
  config.i18n.fallbacks = true
@@ -77,3 +88,4 @@ Rails.application.configure do
77
88
  # Do not dump schema after migrations.
78
89
  config.active_record.dump_schema_after_migration = false
79
90
  end
91
+
@@ -1,15 +1,8 @@
1
1
  Authenticate.configure do |config|
2
- # config.user_model = 'User'
3
- # config.cookie_name = 'authenticate_session_token'
4
- # config.cookie_expiration = { 1.month.from_now.utc }
5
- # config.cookie_domain = nil
6
- # config.cookie_path = '/'
7
- # config.secure_cookie = false # set to true in production https environments
8
- # config.http_only = false # set to true if you can
9
2
  config.timeout_in = 45.minutes
10
- config.max_session_lifetime = 5.minutes
11
- config.max_consecutive_bad_logins_allowed = 1
12
- config.bad_login_lockout_period = 2.minutes
3
+ config.max_session_lifetime = 20.minutes
4
+ config.max_consecutive_bad_logins_allowed = 2
5
+ config.bad_login_lockout_period = 10.minutes
13
6
  config.reset_password_within = 5.minutes
14
- # config.authentication_strategy = :email
7
+ config.password_length = 8..128
15
8
  end
@@ -1,4 +1,7 @@
1
1
  Rails.application.routes.draw do
2
+ resources :welcome, only: [:index]
3
+ root 'welcome#index'
4
+
2
5
  # The priority is based upon order of creation: first created -> highest priority.
3
6
  # See how all your routes lay out with "rake routes".
4
7
 
Binary file
@@ -1,5 +1,3 @@
1
- require 'authenticate/user'
2
-
3
1
  FactoryGirl.define do
4
2
  sequence :email do |n|
5
3
  "user#{n}@example.com"
@@ -7,7 +5,6 @@ FactoryGirl.define do
7
5
 
8
6
  factory :user do
9
7
  email
10
- # encrypted_password 'password'
11
8
  password 'password'
12
9
 
13
10
  trait :without_email do
@@ -23,8 +20,9 @@ FactoryGirl.define do
23
20
  session_token 'this_is_a_big_fake_long_token'
24
21
  end
25
22
 
26
- trait :with_forgotten_password do
23
+ trait :with_password_reset_token_and_timestamp do
27
24
  password_reset_token Authenticate::Token.new
25
+ password_reset_sent_at 10.seconds.ago
28
26
  end
29
27
  end
30
28
  end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'support/features/feature_helpers'
3
+
4
+ feature 'visitor has consecutive bad logins' do
5
+ before do
6
+ # puts Authenticate.configuration.max_consecutive_bad_logins_allowed.inspect
7
+ # puts Authenticate.configuration.bad_login_lockout_period.inspect
8
+ @user = create(:user)
9
+ end
10
+
11
+ scenario 'less than max bad logins does not lock account' do
12
+ sign_in_with @user.email, 'badpassword'
13
+ sign_in_with @user.email, 'badpassword'
14
+ sign_in_with @user.email, @user.password
15
+
16
+ expect_user_to_be_signed_in
17
+ end
18
+
19
+ scenario 'exceeds max bad logins and locks account' do
20
+ sign_in_with @user.email, 'badpassword'
21
+ sign_in_with @user.email, 'badpassword'
22
+ sign_in_with @user.email, 'badpassword'
23
+
24
+ expect_locked_account
25
+ expect_lockout_time_to_be_displayed
26
+ expect_user_to_be_signed_out
27
+ end
28
+
29
+ scenario 'user locks account, waits for lock to expire, logs in successfully' do
30
+ sign_in_with @user.email, 'badpassword'
31
+ sign_in_with @user.email, 'badpassword'
32
+ sign_in_with @user.email, 'badpassword'
33
+
34
+ Timecop.travel 50.minutes do
35
+ sign_in_with @user.email, @user.password
36
+ expect_user_to_be_signed_in
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+
43
+ def expect_locked_account
44
+ expect(page).to have_content 'Your account is locked'
45
+ end
46
+
47
+ def expect_lockout_time_to_be_displayed
48
+ expect(page).to have_content '10 minutes'
49
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'support/features/feature_helpers'
3
+
4
+ feature 'visitor has consecutive bad logins' do
5
+ before(:each) do
6
+ @user = create(:user)
7
+ end
8
+
9
+ scenario 'visitor logs in and subsequent click within lifetime' 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 and lets session live too long' 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