aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/test/cases/secure_password_test.rb
blob: 9ce9f696f0b59bad7c5b8ab800c755abbc2f8973 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# frozen_string_literal: true

require "cases/helper"
require "models/user"
require "models/visitor"

class SecurePasswordTest < ActiveSupport::TestCase
  setup do
    # Used only to speed up tests
    @original_min_cost = ActiveModel::SecurePassword.min_cost
    ActiveModel::SecurePassword.min_cost = true

    @user = User.new
    @visitor = Visitor.new

    # Simulate loading an existing user from the DB
    @existing_user = User.new
    @existing_user.password_digest = BCrypt::Password.create("password", cost: BCrypt::Engine::MIN_COST)
  end

  teardown do
    ActiveModel::SecurePassword.min_cost = @original_min_cost
  end

  test "automatically include ActiveModel::Validations when validations are enabled" do
    assert_respond_to @user, :valid?
  end

  test "don't include ActiveModel::Validations when validations are disabled" do
    assert_not_respond_to @visitor, :valid?
  end

  test "create a new user with validations and valid password/confirmation" do
    @user.password = "password"
    @user.password_confirmation = "password"

    assert @user.valid?(:create), "user should be valid"

    @user.password = "a" * 72
    @user.password_confirmation = "a" * 72

    assert @user.valid?(:create), "user should be valid"
  end

  test "create a new user with validation and a spaces only password" do
    @user.password = " " * 72
    assert @user.valid?(:create), "user should be valid"
  end

  test "create a new user with validation and a blank password" do
    @user.password = ""
    assert_not @user.valid?(:create), "user should be invalid"
    assert_equal 1, @user.errors.count
    assert_equal ["can't be blank"], @user.errors[:password]
  end

  test "create a new user with validation and a nil password" do
    @user.password = nil
    assert_not @user.valid?(:create), "user should be invalid"
    assert_equal 1, @user.errors.count
    assert_equal ["can't be blank"], @user.errors[:password]
  end

  test "create a new user with validation and password length greater than 72" do
    @user.password = "a" * 73
    @user.password_confirmation = "a" * 73
    assert_not @user.valid?(:create), "user should be invalid"
    assert_equal 1, @user.errors.count
    assert_equal ["is too long (maximum is 72 characters)"], @user.errors[:password]
  end

  test "create a new user with validation and a blank password confirmation" do
    @user.password = "password"
    @user.password_confirmation = ""
    assert_not @user.valid?(:create), "user should be invalid"
    assert_equal 1, @user.errors.count
    assert_equal ["doesn't match Password"], @user.errors[:password_confirmation]
  end

  test "create a new user with validation and a nil password confirmation" do
    @user.password = "password"
    @user.password_confirmation = nil
    assert @user.valid?(:create), "user should be valid"
  end

  test "create a new user with validation and an incorrect password confirmation" do
    @user.password = "password"
    @user.password_confirmation = "something else"
    assert_not @user.valid?(:create), "user should be invalid"
    assert_equal 1, @user.errors.count
    assert_equal ["doesn't match Password"], @user.errors[:password_confirmation]
  end

  test "update an existing user with validation and no change in password" do
    assert @existing_user.valid?(:update), "user should be valid"
  end

  test "update an existing user with validations and valid password/confirmation" do
    @existing_user.password = "password"
    @existing_user.password_confirmation = "password"

    assert @existing_user.valid?(:update), "user should be valid"

    @existing_user.password = "a" * 72
    @existing_user.password_confirmation = "a" * 72

    assert @existing_user.valid?(:update), "user should be valid"
  end

  test "updating an existing user with validation and a blank password" do
    @existing_user.password = ""
    assert @existing_user.valid?(:update), "user should be valid"
  end

  test "updating an existing user with validation and a spaces only password" do
    @user.password = " " * 72
    assert @user.valid?(:update), "user should be valid"
  end

  test "updating an existing user with validation and a blank password and password_confirmation" do
    @existing_user.password = ""
    @existing_user.password_confirmation = ""
    assert @existing_user.valid?(:update), "user should be valid"
  end

  test "updating an existing user with validation and a nil password" do
    @existing_user.password = nil
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["can't be blank"], @existing_user.errors[:password]
  end

  test "updating an existing user with validation and password length greater than 72" do
    @existing_user.password = "a" * 73
    @existing_user.password_confirmation = "a" * 73
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["is too long (maximum is 72 characters)"], @existing_user.errors[:password]
  end

  test "updating an existing user with validation and a blank password confirmation" do
    @existing_user.password = "password"
    @existing_user.password_confirmation = ""
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["doesn't match Password"], @existing_user.errors[:password_confirmation]
  end

  test "updating an existing user with validation and a nil password confirmation" do
    @existing_user.password = "password"
    @existing_user.password_confirmation = nil
    assert @existing_user.valid?(:update), "user should be valid"
  end

  test "updating an existing user with validation and an incorrect password confirmation" do
    @existing_user.password = "password"
    @existing_user.password_confirmation = "something else"
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["doesn't match Password"], @existing_user.errors[:password_confirmation]
  end

  test "updating an existing user with validation and a blank password digest" do
    @existing_user.password_digest = ""
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["can't be blank"], @existing_user.errors[:password]
  end

  test "updating an existing user with validation and a nil password digest" do
    @existing_user.password_digest = nil
    assert_not @existing_user.valid?(:update), "user should be invalid"
    assert_equal 1, @existing_user.errors.count
    assert_equal ["can't be blank"], @existing_user.errors[:password]
  end

  test "setting a blank password should not change an existing password" do
    @existing_user.password = ""
    assert @existing_user.password_digest == "password"
  end

  test "setting a nil password should clear an existing password" do
    @existing_user.password = nil
    assert_nil @existing_user.password_digest
  end

  test "override secure password attribute" do
    assert_nil @user.password_called

    @user.password = "secret"

    assert_equal "secret", @user.password
    assert_equal 1, @user.password_called

    @user.password = "terces"

    assert_equal "terces", @user.password
    assert_equal 2, @user.password_called
  end

  test "authenticate" do
    @user.password = "secret"
    @user.recovery_password = "42password"

    assert_equal false, @user.authenticate("wrong")
    assert_equal @user, @user.authenticate("secret")

    assert_equal false, @user.authenticate_password("wrong")
    assert_equal @user, @user.authenticate_password("secret")

    assert_equal false, @user.authenticate_recovery_password("wrong")
    assert_equal @user, @user.authenticate_recovery_password("42password")
  end

  test "Password digest cost defaults to bcrypt default cost when min_cost is false" do
    ActiveModel::SecurePassword.min_cost = false

    @user.password = "secret"
    assert_equal BCrypt::Engine::DEFAULT_COST, @user.password_digest.cost
  end

  test "Password digest cost honors bcrypt cost attribute when min_cost is false" do
    original_bcrypt_cost = BCrypt::Engine.cost
    ActiveModel::SecurePassword.min_cost = false
    BCrypt::Engine.cost = 5

    @user.password = "secret"
    assert_equal BCrypt::Engine.cost, @user.password_digest.cost
  ensure
    BCrypt::Engine.cost = original_bcrypt_cost
  end

  test "Password digest cost can be set to bcrypt min cost to speed up tests" do
    ActiveModel::SecurePassword.min_cost = true

    @user.password = "secret"
    assert_equal BCrypt::Engine::MIN_COST, @user.password_digest.cost
  end
end