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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca8c03070d7634ba64d25f575271f6bebe6920fc
4
- data.tar.gz: bc353110b848819716f864a7ca66f7df46afae1b
3
+ metadata.gz: 314ba694f7183f9f3a7ed5c239c1ec1dc7e45298
4
+ data.tar.gz: dabffa423ced67c1f8bf767befdee51979ef3989
5
5
  SHA512:
6
- metadata.gz: 9c0d18c59f7373dbb3c817a92b7b02ff901d5f56c8c7ac5ab72c7c2dac611b044c15b25ebb0b93873986663825b00de386bc1ba25eccae537724d27171c767a1
7
- data.tar.gz: 0122cd40a252a9db8c946368529220bdc41f6750220fd1ad3f6c61df3744392eac0f8969d13365e26f3c124fe333a1451f95e15bfad83574b2386db45d7a4ccc
6
+ metadata.gz: 9e3cc55ad8b83ab460966a9bfd9000c7799ed5792cc0402c8ce90eb22d3f055b0b3dadf146038c46c844f5278e4a233c6237992febf2679ddc95838c0ad0d4b8
7
+ data.tar.gz: 24f500d3c8917867c2a16c7b3ba2f0bcf44c47dfe5ea83ffb949c9bb03c864f880a30ccab3f7e7f755cb4fa7039f12926b42cd01d30fbab9482a7c0851c8ff1a
data/.gitignore CHANGED
@@ -2,9 +2,9 @@
2
2
  authenticate-*.gem
3
3
  log/*.log
4
4
  pkg/
5
- test/dummy/db/*.sqlite3
6
- test/dummy/db/*.sqlite3-journal
7
- test/dummy/log/*.log
8
- test/dummy/tmp/
5
+ spec/dummy/db/*.sqlite3
6
+ spec/dummy/db/*.sqlite3-journal
7
+ spec/dummy/log/*.log
8
+ spec/dummy/tmp/
9
9
  spec/dummy/log/test.log
10
10
  spec/dummy/log/development.log
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Authenticate Changelog
2
2
 
3
+ ## [0.3.0] - February 24, 2016
4
+
5
+ Moved normalize_email and find_normalized_email methods to base User module.
6
+ Added full suite of controller and feature tests.
7
+ Bug fixes:
8
+ * failed login count fix was off by one.
9
+ * password validation now done only in correct circumstances
10
+
11
+ [0.3.0]: https://github.com/tomichj/authenticate/compare/v0.2.2...v0.3.0
12
+
13
+
14
+
3
15
  ## [0.2.3] - February 13, 2016
4
16
 
5
17
  Small bugfix for :username authentication.
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ # gem 'capybara'
5
6
 
6
7
  # Declare your gem's dependencies in authenticate.gemspec.
7
8
  # Bundler will treat runtime dependencies like base dependencies, and
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authenticate (0.2.2)
4
+ authenticate (0.3.0)
5
5
  bcrypt
6
6
  email_validator (~> 1.6)
7
7
  rails (>= 4.0, < 5.1)
@@ -44,20 +44,26 @@ GEM
44
44
  minitest (~> 5.1)
45
45
  thread_safe (~> 0.3, >= 0.3.4)
46
46
  tzinfo (~> 1.1)
47
+ addressable (2.4.0)
47
48
  arel (6.0.3)
48
49
  bcrypt (3.1.10)
49
50
  builder (3.2.2)
51
+ capybara (2.6.2)
52
+ addressable
53
+ mime-types (>= 1.16)
54
+ nokogiri (>= 1.3.3)
55
+ rack (>= 1.0.0)
56
+ rack-test (>= 0.5.4)
57
+ xpath (~> 2.0)
50
58
  coderay (1.1.0)
51
59
  concurrent-ruby (1.0.0)
60
+ database_cleaner (1.5.1)
52
61
  diff-lcs (1.2.5)
53
62
  email_validator (1.6.0)
54
63
  activemodel
55
64
  erubis (2.7.0)
56
65
  factory_girl (4.4.0)
57
66
  activesupport (>= 3.0.0)
58
- factory_girl_rails (4.4.1)
59
- factory_girl (~> 4.4.0)
60
- railties (>= 3.0.0)
61
67
  globalid (0.3.6)
62
68
  activesupport (>= 4.1.0)
63
69
  i18n (0.7.0)
@@ -120,6 +126,8 @@ GEM
120
126
  rspec-mocks (~> 3.1.0)
121
127
  rspec-support (~> 3.1.0)
122
128
  rspec-support (3.1.2)
129
+ shoulda-matchers (2.8.0)
130
+ activesupport (>= 3.0.0)
123
131
  slop (3.6.0)
124
132
  sprockets (3.5.2)
125
133
  concurrent-ruby (~> 1.0)
@@ -131,18 +139,25 @@ GEM
131
139
  sqlite3 (1.3.11)
132
140
  thor (0.19.1)
133
141
  thread_safe (0.3.5)
142
+ timecop (0.8.0)
134
143
  tzinfo (1.2.2)
135
144
  thread_safe (~> 0.1)
145
+ xpath (2.0.0)
146
+ nokogiri (~> 1.3)
136
147
 
137
148
  PLATFORMS
138
149
  ruby
139
150
 
140
151
  DEPENDENCIES
141
152
  authenticate!
142
- factory_girl_rails
153
+ capybara (~> 2.6.2)
154
+ database_cleaner (~> 1.5.1)
155
+ factory_girl
143
156
  pry
144
- rspec-rails
157
+ rspec-rails (~> 3.1.0)
158
+ shoulda-matchers (~> 2.8)
145
159
  sqlite3
160
+ timecop (~> 0.8.0)
146
161
 
147
162
  BUNDLED WITH
148
163
  1.11.2
@@ -68,7 +68,7 @@ class Authenticate::PasswordsController < Authenticate::AuthenticateController
68
68
  end
69
69
 
70
70
  def find_user_for_create
71
- Authenticate.configuration.user_model_class.find_by_email params[:password][:email]
71
+ Authenticate.configuration.user_model_class.find_by_normalized_email params[:password][:email]
72
72
  end
73
73
 
74
74
  def find_user_for_edit
data/authenticate.gemspec CHANGED
@@ -25,11 +25,15 @@ Gem::Specification.new do |s|
25
25
  s.add_dependency 'email_validator', '~> 1.6'
26
26
  s.add_dependency 'rails', '>= 4.0', '< 5.1'
27
27
 
28
- # s.add_development_dependency 'capybara'
29
- s.add_development_dependency 'factory_girl_rails'
30
- s.add_development_dependency 'rspec-rails'
28
+ # s.add_development_dependency 'factory_girl_rails', '~> 4.4.1'
29
+ s.add_development_dependency 'factory_girl'
30
+ s.add_development_dependency 'rspec-rails', '~> 3.1.0'
31
31
  s.add_development_dependency 'pry'
32
32
  s.add_development_dependency 'sqlite3'
33
+ s.add_development_dependency 'shoulda-matchers', '~> 2.8'
34
+ s.add_development_dependency 'capybara', '~> 2.6.2'
35
+ s.add_development_dependency 'database_cleaner', '~> 1.5.1'
36
+ s.add_development_dependency 'timecop', '~> 0.8.0'
33
37
 
34
38
  s.required_ruby_version = Gem::Requirement.new('>= 2.0')
35
39
  end
@@ -56,7 +56,7 @@ en:
56
56
  title: Sign up
57
57
  callbacks:
58
58
  authenticatable:
59
- failure: Bad id or password
59
+ failure: Invalid id or password
60
60
  brute_force:
61
61
  failure: "Your account is locked, will unlock in %{time_remaining}"
62
62
  lifetimed:
@@ -2,8 +2,13 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem 'shoulda-matchers', '~> 2.8'
6
+ gem 'capybara', '~> 2.6.2'
7
+ gem 'database_cleaner', '~> 1.5.1'
8
+ gem 'timecop', '~> 0.8.0'
9
+
5
10
  gem "bundler", "~> 1.3"
6
- gem "factory_girl_rails", "~> 4.2"
11
+ gem "factory_girl", "~> 4.4"
7
12
  gem "rspec-rails", "~> 3.1"
8
13
  gem "sqlite3", "~> 1.3"
9
14
  gem "pry", :require => false
@@ -6,7 +6,7 @@ Authenticate.lifecycle.prepend_after_authentication name: 'brute force protectio
6
6
  unless session.authenticated? || Authenticate.configuration.max_consecutive_bad_logins_allowed.nil?
7
7
  user_credentials = User.credentials(session.request.params)
8
8
  user ||= User.find_by_credentials(user_credentials)
9
- if user
9
+ if user && user.respond_to?(:register_failed_login!)
10
10
  user.register_failed_login!
11
11
  user.save!
12
12
  end
@@ -14,14 +14,13 @@ Authenticate.lifecycle.prepend_after_authentication name: 'brute force protectio
14
14
 
15
15
  # if user is locked, and we allow a lockout period, then unlock the user if they've waited
16
16
  # longer than the lockout period.
17
- if user && !Authenticate.configuration.bad_login_lockout_period.nil? && user.locked?
17
+ if user && user.respond_to?(:locked?) && user.locked? && !Authenticate.configuration.bad_login_lockout_period.nil?
18
18
  user.unlock! if user.lock_expires_at <= Time.now.utc
19
19
  end
20
20
 
21
21
  # if the user is still locked, let them know how long they are locked for.
22
- if user && user.locked?
22
+ if user && user.respond_to?(:locked?) && user.locked?
23
23
  remaining = time_ago_in_words(user.lock_expires_at)
24
- # throw(:failure, "Your account is locked, will unlock in #{remaining.to_s}")
25
24
  throw(:failure, I18n.t('callbacks.brute_force.failure', time_remaining: remaining.to_s))
26
25
  end
27
26
 
@@ -206,12 +206,12 @@ module Authenticate
206
206
 
207
207
  def user_model_route_key
208
208
  return :users if @user_model == '::User' # avoid nil in generator
209
- Authenticate.configuration.user_model_class.model_name.route_key
209
+ user_model_class.model_name.route_key
210
210
  end
211
211
 
212
212
  def user_model_param_key
213
213
  return :user if @user_model == '::User' # avoid nil in generator
214
- Authenticate.configuration.user_model_class.model_name.param_key
214
+ user_model_class.model_name.param_key
215
215
  end
216
216
 
217
217
  # The name of foreign key parameter for the configured user model.
@@ -12,9 +12,7 @@ module Authenticate
12
12
  # Validate a user's identity with (typically) email/ID & password, and return the User if valid, or nil.
13
13
  # After calling this, call login(user) to complete the process.
14
14
  def authenticate(params)
15
- # todo: get params from User model
16
15
  credentials = Authenticate.configuration.user_model_class.credentials(params)
17
- debug "Controller::credentials: #{credentials.inspect}"
18
16
  Authenticate.configuration.user_model_class.authenticate(credentials)
19
17
  end
20
18
 
@@ -102,6 +100,7 @@ module Authenticate
102
100
 
103
101
  # User is not authorized, bounce 'em to sign in
104
102
  def unauthorized(msg = t('flashes.failure_when_not_signed_in'))
103
+ authenticate_session.deauthenticate
105
104
  respond_to do |format|
106
105
  format.any(:js, :json, :xml) { head :unauthorized }
107
106
  format.any {
@@ -33,7 +33,7 @@ module Authenticate
33
33
  module BruteForce
34
34
  extend ActiveSupport::Concern
35
35
 
36
- def self.required_fields(klass)
36
+ def self.required_fields(_klass)
37
37
  [:failed_logins_count, :lock_expires_at]
38
38
  end
39
39
 
@@ -41,7 +41,7 @@ module Authenticate
41
41
  def register_failed_login!
42
42
  self.failed_logins_count ||= 0
43
43
  self.failed_logins_count += 1
44
- lock! if self.failed_logins_count >= max_bad_logins
44
+ lock! if self.failed_logins_count > max_bad_logins
45
45
  end
46
46
 
47
47
  def lock!
@@ -26,7 +26,7 @@ module Authenticate
26
26
  module DbPassword
27
27
  extend ActiveSupport::Concern
28
28
 
29
- def self.required_fields(klass)
29
+ def self.required_fields(_klass)
30
30
  [:encrypted_password]
31
31
  end
32
32
 
@@ -72,8 +72,7 @@ module Authenticate
72
72
 
73
73
  # If we already have an encrypted password and it's not changing, skip the validation.
74
74
  def skip_password_validation?
75
- # encrypted_password.present? && !password_changing
76
- false
75
+ encrypted_password.present? && !password_changing
77
76
  end
78
77
 
79
78
  end
@@ -25,7 +25,7 @@ module Authenticate
25
25
  module Email
26
26
  extend ActiveSupport::Concern
27
27
 
28
- def self.required_fields(klass)
28
+ def self.required_fields(_klass)
29
29
  [:email]
30
30
  end
31
31
 
@@ -41,6 +41,7 @@ module Authenticate
41
41
  module ClassMethods
42
42
 
43
43
  def credentials(params)
44
+ return [] if params.nil? || params[:session].nil?
44
45
  [params[:session][:email], params[:session][:password]]
45
46
  end
46
47
 
@@ -51,11 +52,7 @@ module Authenticate
51
52
 
52
53
  def find_by_credentials(credentials)
53
54
  email = credentials[0]
54
- find_by_email normalize_email(email)
55
- end
56
-
57
- def normalize_email(email)
58
- email.to_s.downcase.gsub(/\s+/, '')
55
+ find_by_normalized_email(email)
59
56
  end
60
57
 
61
58
  end
@@ -25,7 +25,7 @@ module Authenticate
25
25
  module Lifetimed
26
26
  extend ActiveSupport::Concern
27
27
 
28
- def self.required_fields(klass)
28
+ def self.required_fields(_klass)
29
29
  [:current_sign_in_at]
30
30
  end
31
31
 
@@ -16,7 +16,7 @@ module Authenticate
16
16
  module PasswordReset
17
17
  extend ActiveSupport::Concern
18
18
 
19
- def self.required_fields(klass)
19
+ def self.required_fields(_klass)
20
20
  [:password_reset_token, :password_reset_sent_at, :email]
21
21
  end
22
22
 
@@ -29,7 +29,7 @@ module Authenticate
29
29
  module Timeoutable
30
30
  extend ActiveSupport::Concern
31
31
 
32
- def self.required_fields(klass)
32
+ def self.required_fields(_klass)
33
33
  [:last_access_at]
34
34
  end
35
35
 
@@ -45,6 +45,6 @@ module Authenticate
45
45
  def timeout_in
46
46
  Authenticate.configuration.timeout_in
47
47
  end
48
- end
48
+ end
49
49
  end
50
50
  end
@@ -19,7 +19,7 @@ module Authenticate
19
19
  module Trackable
20
20
  extend ActiveSupport::Concern
21
21
 
22
- def self.required_fields(klass)
22
+ def self.required_fields(_klass)
23
23
  [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
24
24
  end
25
25
 
@@ -17,7 +17,7 @@ module Authenticate
17
17
  module Username
18
18
  extend ActiveSupport::Concern
19
19
 
20
- def self.required_fields(klass)
20
+ def self.required_fields(_klass)
21
21
  [:username, :email]
22
22
  end
23
23
 
@@ -21,8 +21,6 @@ module Authenticate
21
21
  def login(user, &block)
22
22
  debug 'session.login()'
23
23
  @current_user = user
24
- debug "session.login @current_user: #{@current_user.inspect}"
25
- # todo extract token gen to two different strategies
26
24
  @current_user.generate_session_token if user.present?
27
25
 
28
26
  message = catch(:failure) do
@@ -44,7 +42,6 @@ module Authenticate
44
42
  end
45
43
  end
46
44
 
47
-
48
45
  # Get the user represented by this session.
49
46
  #
50
47
  # @return [User]
@@ -64,7 +61,6 @@ module Authenticate
64
61
  current_user.present?
65
62
  end
66
63
 
67
-
68
64
  # Invalidate the session token, unset the current user and remove the cookie.
69
65
  #
70
66
  # @return [void]
@@ -49,6 +49,18 @@ module Authenticate
49
49
  save validate: false
50
50
  end
51
51
 
52
+ module ClassMethods
53
+
54
+ def normalize_email(email)
55
+ email.to_s.downcase.gsub(/\s+/, '')
56
+ end
57
+
58
+ # We need to find users by email even if they don't use email to log in
59
+ def find_by_normalized_email(email)
60
+ find_by_email normalize_email(email)
61
+ end
62
+
63
+ end
52
64
 
53
65
  end
54
66
  end
@@ -1,3 +1,3 @@
1
1
  module Authenticate
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+ require 'support/controllers/controller_helpers'
3
+
4
+ describe Authenticate::PasswordsController, type: :controller do
5
+ it { is_expected.to be_a Authenticate::Controller }
6
+
7
+ describe 'get to #new' do
8
+ it 'renders the new form' do
9
+ get :new
10
+ expect(response).to be_success
11
+ expect(response).to render_template(:new)
12
+ end
13
+ end
14
+
15
+ describe 'post to #create' do
16
+ context 'with email for an existing user' do
17
+ it 'generates a password_reset_token' do
18
+ user = create(:user)
19
+ post :create, password: { email: user.email.upcase }
20
+ expect(user.reload.password_reset_token).not_to be_nil
21
+ end
22
+ it 'sends a password reset email' do
23
+ ActionMailer::Base.deliveries.clear
24
+ user = create(:user)
25
+ post :create, password: { email: user.email }
26
+ email = ActionMailer::Base.deliveries.last
27
+ expect(email.subject).to match(/change your password/i)
28
+ end
29
+ end
30
+ context 'with email that does not belong to an existing user' do
31
+ bad_email = 'bunk_email_address@non_existent_domain.com'
32
+ it 'does not send an email' do
33
+ ActionMailer::Base.deliveries.clear
34
+ post :create, password: { email: bad_email}
35
+ expect(ActionMailer::Base.deliveries).to be_empty
36
+ end
37
+ it 'always responds with redirect to avoid leaking user information' do
38
+ post :create, password: { email: bad_email }
39
+ expect(response).to be_redirect
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'get to #edit' do
45
+ context 'with a valid password_reset_token and timestamp' do
46
+ it 'renders password update form' do
47
+ user = create(:user, :with_password_reset_token_and_timestamp)
48
+ get :edit, id: user.id, token: user.password_reset_token
49
+ expect(response).to be_success
50
+ expect(response).to render_template(:edit)
51
+ expect(assigns(:user)).to eq user
52
+ end
53
+ end
54
+ context 'with a valid timestamp but invalid password_reset_token' do
55
+ it 'renders #new password form with notice' do
56
+ user = create(:user, :with_password_reset_token_and_timestamp)
57
+ get :edit, id: user.id, token: 'bad token'
58
+ expect(response).to be_success
59
+ expect(response).to render_template(:new)
60
+ end
61
+ end
62
+ context 'with a valid password_reset_token but invalid timestamp' do
63
+ it 'renders #new password form with notice' do
64
+ user = create(:user, :with_password_reset_token_and_timestamp, password_reset_sent_at: 2.years.ago)
65
+ get :edit, id: user.id, token: user.password_reset_token
66
+ expect(response).to be_redirect
67
+ expect(flash[:notice]).to match /password change request has expired/
68
+ end
69
+ end
70
+ context 'with a blank password_reset_token' do
71
+ it 'renders #new password form with notice' do
72
+ user = create(:user)
73
+ get :edit, id: user.id, token: nil
74
+ expect(response).to be_success
75
+ expect(response).to render_template(:new)
76
+ end
77
+ end
78
+ end
79
+
80
+ describe 'put to #update' do
81
+ context 'with valid password_reset_token and new password' do
82
+ it 'updates the user password' do
83
+ user = create(:user, :with_password_reset_token_and_timestamp)
84
+ old_encrypted_password = user.encrypted_password
85
+ put :update, update_params(user, new_password: 'new_password')
86
+ expect(user.reload.encrypted_password).not_to eq old_encrypted_password
87
+ end
88
+ it 'signs in the user' do
89
+ user = create(:user, :with_password_reset_token_and_timestamp)
90
+ put :update, update_params(user, new_password: 'new_password')
91
+ expect(cookies[:authenticate_session_token]).to be_present
92
+ expect(cookies[:authenticate_session_token]).to eq user.reload.session_token
93
+ end
94
+ it 'redirects user' do
95
+ user = create(:user, :with_password_reset_token_and_timestamp)
96
+ put :update, update_params(user, new_password: 'new_password')
97
+ expect(response).to redirect_to(Authenticate.configuration.redirect_url)
98
+ end
99
+ end
100
+ context 'with invalid new password' do
101
+ it 're-renders password edit form' do
102
+ user = create(:user, :with_password_reset_token_and_timestamp)
103
+ put :update, update_params(user, new_password: 'short')
104
+ expect(response).to render_template(:edit)
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ def update_params(user, options = {})
111
+ new_password = options.fetch(:new_password)
112
+ {
113
+ id: user,
114
+ token: user.password_reset_token,
115
+ password_reset: { password: new_password }
116
+ }
117
+ end
118
+
119
+ end