rodauth 1.22.0 → 1.23.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +12 -0
  3. data/README.rdoc +5 -3
  4. data/doc/email_base.rdoc +1 -0
  5. data/doc/release_notes/1.23.0.txt +32 -0
  6. data/lib/rodauth.rb +5 -2
  7. data/lib/rodauth/features/base.rb +8 -0
  8. data/lib/rodauth/features/change_password_notify.rb +1 -1
  9. data/lib/rodauth/features/create_account.rb +1 -1
  10. data/lib/rodauth/features/email_auth.rb +3 -4
  11. data/lib/rodauth/features/email_base.rb +7 -2
  12. data/lib/rodauth/features/lockout.rb +1 -1
  13. data/lib/rodauth/features/login.rb +6 -2
  14. data/lib/rodauth/features/otp.rb +6 -3
  15. data/lib/rodauth/features/password_expiration.rb +1 -1
  16. data/lib/rodauth/features/recovery_codes.rb +3 -3
  17. data/lib/rodauth/features/reset_password.rb +2 -2
  18. data/lib/rodauth/features/sms_codes.rb +5 -5
  19. data/lib/rodauth/features/verify_account.rb +2 -2
  20. data/lib/rodauth/features/verify_login_change.rb +1 -1
  21. data/lib/rodauth/version.rb +1 -1
  22. data/templates/email-auth-request-form.str +2 -2
  23. data/templates/reset-password-request.str +3 -3
  24. data/templates/unlock-account-request.str +3 -3
  25. data/templates/verify-account-resend.str +3 -3
  26. metadata +5 -43
  27. data/Rakefile +0 -179
  28. data/spec/account_expiration_spec.rb +0 -225
  29. data/spec/all.rb +0 -1
  30. data/spec/change_login_spec.rb +0 -156
  31. data/spec/change_password_notify_spec.rb +0 -33
  32. data/spec/change_password_spec.rb +0 -202
  33. data/spec/close_account_spec.rb +0 -162
  34. data/spec/confirm_password_spec.rb +0 -70
  35. data/spec/create_account_spec.rb +0 -127
  36. data/spec/disallow_common_passwords_spec.rb +0 -93
  37. data/spec/disallow_password_reuse_spec.rb +0 -179
  38. data/spec/email_auth_spec.rb +0 -285
  39. data/spec/http_basic_auth_spec.rb +0 -143
  40. data/spec/jwt_cors_spec.rb +0 -57
  41. data/spec/jwt_refresh_spec.rb +0 -256
  42. data/spec/jwt_spec.rb +0 -235
  43. data/spec/lockout_spec.rb +0 -250
  44. data/spec/login_spec.rb +0 -328
  45. data/spec/migrate/001_tables.rb +0 -184
  46. data/spec/migrate/002_account_password_hash_column.rb +0 -11
  47. data/spec/migrate_password/001_tables.rb +0 -73
  48. data/spec/migrate_travis/001_tables.rb +0 -141
  49. data/spec/password_complexity_spec.rb +0 -109
  50. data/spec/password_expiration_spec.rb +0 -244
  51. data/spec/password_grace_period_spec.rb +0 -93
  52. data/spec/remember_spec.rb +0 -451
  53. data/spec/reset_password_spec.rb +0 -229
  54. data/spec/rodauth_spec.rb +0 -343
  55. data/spec/session_expiration_spec.rb +0 -58
  56. data/spec/single_session_spec.rb +0 -127
  57. data/spec/spec_helper.rb +0 -327
  58. data/spec/two_factor_spec.rb +0 -1462
  59. data/spec/update_password_hash_spec.rb +0 -40
  60. data/spec/verify_account_grace_period_spec.rb +0 -171
  61. data/spec/verify_account_spec.rb +0 -240
  62. data/spec/verify_change_login_spec.rb +0 -46
  63. data/spec/verify_login_change_spec.rb +0 -232
  64. data/spec/views/layout-other.str +0 -11
  65. data/spec/views/layout.str +0 -11
  66. data/spec/views/login.str +0 -21
@@ -1,229 +0,0 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
2
-
3
- describe 'Rodauth reset_password feature' do
4
- it "should support resetting passwords for accounts" do
5
- last_sent_column = nil
6
- rodauth do
7
- enable :login, :reset_password
8
- reset_password_email_last_sent_column{last_sent_column}
9
- end
10
- roda do |r|
11
- r.rodauth
12
- r.root{view :content=>""}
13
- end
14
-
15
- login(:login=>'foo@example2.com', :pass=>'01234567')
16
- page.html.wont_match(/notice_flash/)
17
-
18
- login(:pass=>'01234567', :visit=>false)
19
-
20
- click_button 'Request Password Reset'
21
- page.find('#notice_flash').text.must_equal "An email has been sent to you with a link to reset the password for your account"
22
- page.current_path.must_equal '/'
23
- link = email_link(/(\/reset-password\?key=.+)$/)
24
-
25
- visit link[0...-1]
26
- page.find('#error_flash').text.must_equal "There was an error resetting your password: invalid or expired password reset key"
27
-
28
- visit '/login'
29
- click_link 'Forgot Password?'
30
- fill_in 'Login', :with=>'foo@example.com'
31
- click_button 'Request Password Reset'
32
- email_link(/(\/reset-password\?key=.+)$/).must_equal link
33
-
34
- login(:pass=>'01234567')
35
- click_button 'Request Password Reset'
36
- email_link(/(\/reset-password\?key=.+)$/).must_equal link
37
-
38
- last_sent_column = :email_last_sent
39
- login(:pass=>'01234567')
40
- click_button 'Request Password Reset'
41
- page.find('#error_flash').text.must_equal "An email has recently been sent to you with a link to reset your password"
42
- Mail::TestMailer.deliveries.must_equal []
43
-
44
- DB[:account_password_reset_keys].update(:email_last_sent => Time.now - 250).must_equal 1
45
- login(:pass=>'01234567')
46
- click_button 'Request Password Reset'
47
- page.find('#error_flash').text.must_equal "An email has recently been sent to you with a link to reset your password"
48
- Mail::TestMailer.deliveries.must_equal []
49
-
50
- DB[:account_password_reset_keys].update(:email_last_sent => Time.now - 350).must_equal 1
51
- login(:pass=>'01234567')
52
- click_button 'Request Password Reset'
53
- email_link(/(\/reset-password\?key=.+)$/).must_equal link
54
-
55
- visit link
56
- page.title.must_equal 'Reset Password'
57
-
58
- fill_in 'Password', :with=>'0123456'
59
- fill_in 'Confirm Password', :with=>'0123456789'
60
- click_button 'Reset Password'
61
- page.html.must_include("passwords do not match")
62
- page.find('#error_flash').text.must_equal "There was an error resetting your password"
63
- page.current_path.must_equal '/reset-password'
64
-
65
- fill_in 'Password', :with=>'0123456789'
66
- fill_in 'Confirm Password', :with=>'0123456789'
67
- click_button 'Reset Password'
68
- page.body.must_include 'invalid password, same as current password'
69
- page.find('#error_flash').text.must_equal "There was an error resetting your password"
70
- page.current_path.must_equal '/reset-password'
71
-
72
- fill_in 'Password', :with=>'012'
73
- fill_in 'Confirm Password', :with=>'012'
74
- click_button 'Reset Password'
75
- page.html.must_include("invalid password, does not meet requirements")
76
- page.find('#error_flash').text.must_equal "There was an error resetting your password"
77
- page.current_path.must_equal '/reset-password'
78
-
79
- fill_in 'Password', :with=>'0123456'
80
- fill_in 'Confirm Password', :with=>'0123456'
81
- click_button 'Reset Password'
82
- page.find('#notice_flash').text.must_equal "Your password has been reset"
83
- page.current_path.must_equal '/'
84
-
85
- login(:pass=>'0123456')
86
- page.current_path.must_equal '/'
87
-
88
- login(:pass=>'bad')
89
- click_link "Forgot Password?"
90
- fill_in "Login", :with=>"foo@example.com"
91
- click_button "Request Password Reset"
92
- DB[:account_password_reset_keys].update(:deadline => Time.now - 60).must_equal 1
93
- link = email_link(/(\/reset-password\?key=.+)$/)
94
- visit link
95
- page.find('#error_flash').text.must_equal "There was an error resetting your password: invalid or expired password reset key"
96
- end
97
-
98
- it "should support resetting passwords for accounts without confirmation" do
99
- rodauth do
100
- enable :login, :reset_password
101
- require_password_confirmation? false
102
- end
103
- roda do |r|
104
- r.rodauth
105
- r.root{view :content=>""}
106
- end
107
-
108
- visit '/login'
109
- login(:pass=>'01234567', :visit=>false)
110
- click_button 'Request Password Reset'
111
- page.find('#notice_flash').text.must_equal "An email has been sent to you with a link to reset the password for your account"
112
-
113
- link = email_link(/(\/reset-password\?key=.+)$/)
114
- visit link
115
- fill_in 'Password', :with=>'0123456'
116
- click_button 'Reset Password'
117
- page.find('#notice_flash').text.must_equal "Your password has been reset"
118
- end
119
-
120
- it "should support autologin when resetting passwords for accounts" do
121
- rodauth do
122
- enable :login, :reset_password
123
- reset_password_autologin? true
124
- end
125
- roda do |r|
126
- r.rodauth
127
- r.root{view :content=>rodauth.logged_in? ? "Logged In" : "Not Logged"}
128
- end
129
-
130
- login(:pass=>'01234567')
131
-
132
- click_button 'Request Password Reset'
133
- link = email_link(/(\/reset-password\?key=.+)$/)
134
- visit link
135
- fill_in 'Password', :with=>'0123456'
136
- fill_in 'Confirm Password', :with=>'0123456'
137
- click_button 'Reset Password'
138
- page.find('#notice_flash').text.must_equal "Your password has been reset"
139
- page.body.must_include("Logged In")
140
- end
141
-
142
- it "should clear reset password token when closing account" do
143
- rodauth do
144
- enable :login, :reset_password, :close_account
145
- reset_password_autologin? true
146
- end
147
- roda do |r|
148
- r.rodauth
149
- r.root{view :content=>rodauth.logged_in? ? "Logged In" : "Not Logged"}
150
- end
151
-
152
- login(:pass=>'01234567')
153
- click_button 'Request Password Reset'
154
- email_link(/(\/reset-password\?key=.+)$/)
155
-
156
- login
157
-
158
- DB[:account_password_reset_keys].count.must_equal 1
159
- visit '/close-account'
160
- fill_in 'Password', :with=>'0123456789'
161
- click_button 'Close Account'
162
- DB[:account_password_reset_keys].count.must_equal 0
163
- end
164
-
165
- it "should handle uniqueness errors raised when inserting password reset token" do
166
- rodauth do
167
- enable :login, :reset_password
168
- end
169
- roda do |r|
170
- def rodauth.raised_uniqueness_violation(*) super; true; end
171
- r.rodauth
172
- r.root{view :content=>""}
173
- end
174
-
175
- login(:pass=>'01234567')
176
-
177
- click_button 'Request Password Reset'
178
- link = email_link(/(\/reset-password\?key=.+)$/)
179
- visit link
180
-
181
- fill_in 'Password', :with=>'0123456'
182
- fill_in 'Confirm Password', :with=>'0123456'
183
- click_button 'Reset Password'
184
- page.find('#notice_flash').text.must_equal "Your password has been reset"
185
- end
186
-
187
- it "should support resetting passwords for accounts via jwt" do
188
- rodauth do
189
- enable :login, :reset_password
190
- reset_password_email_body{reset_password_email_link}
191
- end
192
- roda(:jwt) do |r|
193
- r.rodauth
194
- end
195
-
196
- res = json_login(:pass=>'1', :no_check=>true)
197
- res.must_equal [401, {"field-error"=>["password", "invalid password"], "error"=>"There was an error logging in"}]
198
-
199
- res = json_request('/reset-password')
200
- res.must_equal [401, {"error"=>"There was an error resetting your password"}]
201
-
202
- res = json_request('/reset-password-request', :login=>'foo@example2.com')
203
- res.must_equal [401, {"error"=>"There was an error requesting a password reset"}]
204
-
205
- res = json_request('/reset-password-request', :login=>'foo@example.com')
206
- res.must_equal [200, {"success"=>"An email has been sent to you with a link to reset the password for your account"}]
207
-
208
- link = email_link(/key=.+$/)
209
- res = json_request('/reset-password', :key=>link[4...-1])
210
- res.must_equal [401, {"error"=>"There was an error resetting your password"}]
211
-
212
- res = json_request('/reset-password', :key=>link[4..-1], :password=>'1', "password-confirm"=>'2')
213
- res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", 'passwords do not match']}]
214
-
215
- res = json_request('/reset-password', :key=>link[4..-1], :password=>'0123456789', "password-confirm"=>'0123456789')
216
- res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", 'invalid password, same as current password']}]
217
-
218
- res = json_request('/reset-password', :key=>link[4..-1], :password=>'1', "password-confirm"=>'1')
219
- res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", "invalid password, does not meet requirements (minimum 6 characters)"]}]
220
-
221
- res = json_request('/reset-password', :key=>link[4..-1], :password=>"\0ab123456", "password-confirm"=>"\0ab123456")
222
- res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", "invalid password, does not meet requirements (contains null byte)"]}]
223
-
224
- res = json_request('/reset-password', :key=>link[4..-1], :password=>'0123456', "password-confirm"=>'0123456')
225
- res.must_equal [200, {"success"=>"Your password has been reset"}]
226
-
227
- json_login(:pass=>'0123456')
228
- end
229
- end
@@ -1,343 +0,0 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
2
-
3
- describe 'Rodauth' do
4
- it "should keep private methods private when overridden" do
5
- rodauth do
6
- use_database_authentication_functions? false
7
- end
8
- roda do |r|
9
- rodauth.use_database_authentication_functions?.to_s
10
- end
11
-
12
- proc{visit '/'}.must_raise NoMethodError
13
- end
14
-
15
- it "should support template_opts" do
16
- rodauth do
17
- enable :login
18
- template_opts(:layout_opts=>{:path=>'spec/views/layout-other.str'})
19
- end
20
- roda do |r|
21
- r.rodauth
22
- end
23
-
24
- visit '/login'
25
- page.title.must_equal 'Foo Login'
26
- end
27
-
28
- it "should support flash_error_key and flash_notice_key" do
29
- rodauth do
30
- enable :login
31
- template_opts(:layout_opts=>{:path=>'spec/views/layout-other.str'})
32
- flash_error_key 'error2'
33
- flash_notice_key 'notice2'
34
- end
35
- roda do |r|
36
- r.rodauth
37
- rodauth.require_login
38
- view(:content=>'', :layout_opts=>{:path=>'spec/views/layout-other.str'})
39
- end
40
-
41
- visit '/'
42
- page.html.must_include 'Please login to continue'
43
- login(:visit=>false)
44
- page.html.must_include 'You have been logged in'
45
- end
46
-
47
- it "should work without preloading the templates" do
48
- @no_precompile = true
49
- rodauth do
50
- enable :login
51
- end
52
- roda do |r|
53
- r.rodauth
54
- end
55
-
56
- visit '/login'
57
- page.title.must_equal 'Login'
58
- end
59
-
60
- it "should warn when using deprecated configuration methods" do
61
- warning = nil
62
- rodauth do
63
- enable :email_auth
64
- (class << self; self end).send(:define_method, :warn) do |*a|
65
- warning = a.first
66
- end
67
- auth_class_eval do
68
- define_method(:warn) do |*a|
69
- warning = a.first
70
- end
71
- end
72
- no_matching_email_auth_key_message 'foo'
73
- end
74
- roda do |r|
75
- rodauth.no_matching_email_auth_key_message
76
- end
77
-
78
- warning.must_equal "Deprecated no_matching_email_auth_key_message method used during configuration, switch to using no_matching_email_auth_key_error_flash"
79
- visit '/'
80
- body.must_equal 'foo'
81
- warning.must_equal "Deprecated no_matching_email_auth_key_message method called at runtime, switch to using no_matching_email_auth_key_error_flash"
82
- end
83
-
84
- it "should pick up template changes if not caching templates" do
85
- begin
86
- @no_freeze = true
87
- cache = true
88
- rodauth do
89
- enable :login
90
- cache_templates{cache}
91
- end
92
- roda do |r|
93
- r.rodauth
94
- end
95
- dir = 'spec/views2'
96
- file = "#{dir}/login.str"
97
- app.plugin :render, :views=>dir, :engine=>'str'
98
- Dir.mkdir(dir) unless File.directory?(dir)
99
-
100
- text = File.read('spec/views/login.str')
101
- File.open(file, 'wb'){|f| f.write text}
102
- visit '/login'
103
- page.all('label').first.text.must_equal 'Login'
104
-
105
- File.open(file, 'wb'){|f| f.write text.gsub('Login', 'Banana')}
106
- visit '/login'
107
- page.all('label').first.text.must_equal 'Login'
108
-
109
- cache = false
110
- visit '/login'
111
- page.all('label').first.text.must_equal 'Banana'
112
- ensure
113
- File.delete(file) if File.file?(file)
114
- Dir.rmdir(dir) if File.directory?(dir)
115
- end
116
- end
117
-
118
- it "should require login to perform certain actions" do
119
- rodauth do
120
- enable :login, :change_password, :change_login, :close_account
121
- end
122
- roda do |r|
123
- r.rodauth
124
-
125
- r.is "a" do
126
- rodauth.require_login
127
- end
128
- end
129
-
130
- visit '/change-password'
131
- page.current_path.must_equal '/login'
132
-
133
- visit '/change-login'
134
- page.current_path.must_equal '/login'
135
-
136
- visit '/close-account'
137
- page.current_path.must_equal '/login'
138
-
139
- visit '/a'
140
- page.current_path.must_equal '/login'
141
- end
142
-
143
- it "should handle case where account is no longer valid during session" do
144
- rodauth do
145
- enable :login, :change_password
146
- already_logged_in{request.redirect '/'}
147
- skip_status_checks? false
148
- end
149
- roda do |r|
150
- r.rodauth
151
-
152
- r.root do
153
- view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")
154
- end
155
- end
156
-
157
- login
158
- page.body.must_include("Logged In")
159
-
160
- DB[:accounts].update(:status_id=>3)
161
- visit '/change-password'
162
- page.current_path.must_equal '/login'
163
- visit '/'
164
- page.body.must_include("Not Logged")
165
- end
166
-
167
- it "should handle cases where you are already logged in on pages that don't expect a login" do
168
- rodauth do
169
- enable :login, :logout, :create_account, :reset_password, :verify_account
170
- already_logged_in{request.redirect '/'}
171
- end
172
- roda do |r|
173
- r.rodauth
174
-
175
- r.root do
176
- view :content=>''
177
- end
178
- end
179
-
180
- login
181
-
182
- visit '/login'
183
- page.current_path.must_equal '/'
184
-
185
- visit '/create-account'
186
- page.current_path.must_equal '/'
187
-
188
- visit '/reset-password'
189
- page.current_path.must_equal '/'
190
-
191
- visit '/verify-account'
192
- page.current_path.must_equal '/'
193
-
194
- visit '/logout'
195
- page.current_path.must_equal '/logout'
196
- end
197
-
198
- it "should have rodauth.features and rodauth.session_value work when not logged in" do
199
- rodauth do
200
- enable :login
201
- end
202
- roda do |r|
203
- "#{rodauth.features.first.inspect}#{rodauth.session_value.inspect}"
204
- end
205
-
206
- visit '/'
207
- page.body.must_equal ':loginnil'
208
- end
209
-
210
- it "should support auth_class_eval for evaluation inside Auth class" do
211
- rodauth do
212
- enable :login
213
- login_label{foo}
214
- auth_class_eval do
215
- def foo
216
- 'Lonig'
217
- end
218
- end
219
- end
220
- roda do |r|
221
- r.rodauth
222
- end
223
-
224
- visit '/login'
225
- fill_in 'Lonig', :with=>'foo@example.com'
226
- end
227
-
228
- it "should support multiple rodauth configurations in an app" do
229
- app = Class.new(Base)
230
- app.plugin(:rodauth, rodauth_opts) do
231
- enable :login
232
- if ENV['RODAUTH_SEPARATE_SCHEMA']
233
- password_hash_table Sequel[:rodauth_test_password][:account_password_hashes]
234
- function_name do |name|
235
- "rodauth_test_password.#{name}"
236
- end
237
- end
238
- end
239
- app.plugin(:rodauth, rodauth_opts.merge(:name=>:r2)) do
240
- enable :logout
241
- end
242
-
243
- if Minitest::HooksSpec::USE_ROUTE_CSRF
244
- app.plugin :route_csrf, Minitest::HooksSpec::ROUTE_CSRF_OPTS
245
- end
246
-
247
- app.route do |r|
248
- if Minitest::HooksSpec::USE_ROUTE_CSRF
249
- check_csrf!
250
- end
251
- r.on 'r1' do
252
- r.rodauth
253
- 'r1'
254
- end
255
- r.on 'r2' do
256
- r.rodauth(:r2)
257
- 'r2'
258
- end
259
- rodauth.session_value.inspect
260
- end
261
- app.freeze
262
- self.app = app
263
-
264
- login(:path=>'/r1/login')
265
- page.body.must_equal DB[:accounts].get(:id).to_s
266
-
267
- visit '/r2/logout'
268
- click_button 'Logout'
269
- page.body.must_equal 'nil'
270
-
271
- visit '/r1/logout'
272
- page.body.must_equal 'r1'
273
- visit '/r2/login'
274
- page.body.must_equal 'r2'
275
- end
276
-
277
- it "should support account_model setting for backwards compatibility" do
278
- warning = nil
279
- rodauth do
280
- enable :login
281
- (class << self; self end).send(:define_method, :warn){|msg| warning = msg}
282
- account_model Sequel::Model(DB[:accounts].select(:id))
283
- end
284
- roda do |r|
285
- "#{rodauth.accounts_table}#{rodauth.account_select.length}"
286
- end
287
-
288
- visit '/'
289
- page.body.must_equal 'accounts1'
290
- warning.must_equal "account_model is deprecated, use db and accounts_table settings"
291
- end
292
-
293
- it "should support account_select setting for choosing account columns" do
294
- rodauth do
295
- enable :login
296
- account_select [:id, :email]
297
- end
298
- roda do |r|
299
- r.rodauth
300
- rodauth.account_from_session
301
- rodauth.account.keys.map(&:to_s).sort.join(' ')
302
- end
303
-
304
- login
305
- page.body.must_equal 'email id'
306
- end
307
-
308
- it "should support :csrf=>false and :flash=>false plugin options" do
309
- c = Class.new(Roda)
310
- c.plugin(:rodauth, :csrf=>false, :flash=>false){}
311
- c.route{}
312
- c.instance_variable_get(:@middleware).length.must_equal 0
313
- c.ancestors.map(&:to_s).wont_include 'Roda::RodaPlugins::Flash::InstanceMethods'
314
- c.ancestors.map(&:to_s).wont_include 'Roda::RodaPlugins::RouteCsrf::InstanceMethods'
315
- end
316
-
317
- it "should inherit rodauth configuration in subclass" do
318
- auth_class = nil
319
- no_freeze!
320
- rodauth{auth_class = auth}
321
- roda(:csrf=>false, :flash=>false){|r|}
322
- Class.new(app).rodauth.must_equal auth_class
323
- end
324
-
325
- it "should use subclass of rodauth configuration if modifying rodauth configuration in subclass" do
326
- auth_class = nil
327
- no_freeze!
328
- rodauth{auth_class = auth; auth_class_eval{def foo; 'foo' end}}
329
- roda{|r| rodauth.foo}
330
- visit '/'
331
- page.html.must_equal 'foo'
332
-
333
- a = Class.new(app)
334
- a.plugin(:rodauth, rodauth_opts){auth_class_eval{def foo; "#{super}bar" end}}
335
- a.rodauth.superclass.must_equal auth_class
336
-
337
- visit '/'
338
- page.html.must_equal 'foo'
339
- self.app = a
340
- visit '/'
341
- page.html.must_equal 'foobar'
342
- end
343
- end