rodauth 0.10.0 → 1.0.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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +146 -0
  3. data/README.rdoc +644 -220
  4. data/Rakefile +99 -11
  5. data/doc/account_expiration.rdoc +55 -0
  6. data/doc/base.rdoc +104 -0
  7. data/doc/change_login.rdoc +29 -0
  8. data/doc/change_password.rdoc +26 -0
  9. data/doc/close_account.rdoc +31 -0
  10. data/doc/confirm_password.rdoc +22 -0
  11. data/doc/create_account.rdoc +34 -0
  12. data/doc/disallow_password_reuse.rdoc +37 -0
  13. data/doc/email_base.rdoc +19 -0
  14. data/doc/jwt.rdoc +35 -0
  15. data/doc/lockout.rdoc +83 -0
  16. data/doc/login.rdoc +27 -0
  17. data/doc/login_password_requirements_base.rdoc +50 -0
  18. data/doc/logout.rdoc +21 -0
  19. data/doc/otp.rdoc +100 -0
  20. data/doc/password_complexity.rdoc +50 -0
  21. data/doc/password_expiration.rdoc +52 -0
  22. data/doc/password_grace_period.rdoc +10 -0
  23. data/doc/recovery_codes.rdoc +60 -0
  24. data/doc/release_notes/1.0.0.txt +443 -0
  25. data/doc/remember.rdoc +82 -0
  26. data/doc/reset_password.rdoc +70 -0
  27. data/doc/session_expiration.rdoc +27 -0
  28. data/doc/single_session.rdoc +43 -0
  29. data/doc/sms_codes.rdoc +119 -0
  30. data/doc/two_factor_base.rdoc +27 -0
  31. data/doc/verify_account.rdoc +70 -0
  32. data/doc/verify_account_grace_period.rdoc +15 -0
  33. data/doc/verify_change_login.rdoc +9 -0
  34. data/lib/roda/plugins/rodauth.rb +3 -262
  35. data/lib/rodauth.rb +260 -0
  36. data/lib/rodauth/features/account_expiration.rb +108 -0
  37. data/lib/rodauth/features/base.rb +479 -0
  38. data/lib/rodauth/features/change_login.rb +77 -0
  39. data/lib/rodauth/features/change_password.rb +66 -0
  40. data/lib/rodauth/features/close_account.rb +82 -0
  41. data/lib/rodauth/features/confirm_password.rb +51 -0
  42. data/lib/rodauth/features/create_account.rb +128 -0
  43. data/lib/rodauth/features/disallow_password_reuse.rb +82 -0
  44. data/lib/rodauth/features/email_base.rb +63 -0
  45. data/lib/rodauth/features/jwt.rb +151 -0
  46. data/lib/rodauth/features/lockout.rb +262 -0
  47. data/lib/rodauth/features/login.rb +61 -0
  48. data/lib/rodauth/features/login_password_requirements_base.rb +123 -0
  49. data/lib/rodauth/features/logout.rb +37 -0
  50. data/lib/rodauth/features/otp.rb +338 -0
  51. data/lib/rodauth/features/password_complexity.rb +89 -0
  52. data/lib/rodauth/features/password_expiration.rb +111 -0
  53. data/lib/rodauth/features/password_grace_period.rb +46 -0
  54. data/lib/rodauth/features/recovery_codes.rb +240 -0
  55. data/lib/rodauth/features/remember.rb +200 -0
  56. data/lib/rodauth/features/reset_password.rb +207 -0
  57. data/lib/rodauth/features/session_expiration.rb +55 -0
  58. data/lib/rodauth/features/single_session.rb +87 -0
  59. data/lib/rodauth/features/sms_codes.rb +498 -0
  60. data/lib/rodauth/features/two_factor_base.rb +135 -0
  61. data/lib/rodauth/features/verify_account.rb +232 -0
  62. data/lib/rodauth/features/verify_account_grace_period.rb +76 -0
  63. data/lib/rodauth/features/verify_change_login.rb +20 -0
  64. data/lib/rodauth/migrations.rb +130 -0
  65. data/lib/rodauth/version.rb +9 -0
  66. data/spec/account_expiration_spec.rb +90 -0
  67. data/spec/all.rb +1 -0
  68. data/spec/change_login_spec.rb +149 -0
  69. data/spec/change_password_spec.rb +177 -0
  70. data/spec/close_account_spec.rb +162 -0
  71. data/spec/confirm_password_spec.rb +70 -0
  72. data/spec/create_account_spec.rb +127 -0
  73. data/spec/disallow_password_reuse_spec.rb +84 -0
  74. data/spec/lockout_spec.rb +228 -0
  75. data/spec/login_spec.rb +188 -0
  76. data/spec/migrate/001_tables.rb +103 -16
  77. data/spec/migrate/002_account_password_hash_column.rb +11 -0
  78. data/spec/migrate_password/001_tables.rb +60 -42
  79. data/spec/migrate_travis/001_tables.rb +116 -0
  80. data/spec/password_complexity_spec.rb +108 -0
  81. data/spec/password_expiration_spec.rb +243 -0
  82. data/spec/password_grace_period_spec.rb +93 -0
  83. data/spec/remember_spec.rb +424 -0
  84. data/spec/reset_password_spec.rb +185 -0
  85. data/spec/rodauth_spec.rb +57 -980
  86. data/spec/session_expiration_spec.rb +58 -0
  87. data/spec/single_session_spec.rb +107 -0
  88. data/spec/spec_helper.rb +202 -0
  89. data/spec/two_factor_spec.rb +1310 -0
  90. data/spec/verify_account_grace_period_spec.rb +135 -0
  91. data/spec/verify_account_spec.rb +142 -0
  92. data/spec/verify_change_login_spec.rb +46 -0
  93. data/spec/views/login.str +2 -2
  94. data/templates/add-recovery-codes.str +2 -0
  95. data/templates/button.str +5 -0
  96. data/templates/change-login.str +5 -18
  97. data/templates/change-password.str +6 -14
  98. data/templates/close-account.str +3 -6
  99. data/templates/confirm-password.str +4 -14
  100. data/templates/create-account.str +6 -30
  101. data/templates/login-confirm-field.str +6 -0
  102. data/templates/login-field.str +6 -0
  103. data/templates/login.str +5 -19
  104. data/templates/logout.str +2 -6
  105. data/templates/otp-auth-code-field.str +6 -0
  106. data/templates/otp-auth.str +8 -0
  107. data/templates/otp-disable.str +6 -0
  108. data/templates/otp-setup.str +21 -0
  109. data/templates/password-confirm-field.str +6 -0
  110. data/templates/password-field.str +6 -0
  111. data/templates/recovery-auth.str +12 -0
  112. data/templates/recovery-codes.str +6 -0
  113. data/templates/remember.str +8 -12
  114. data/templates/reset-password-request.str +2 -2
  115. data/templates/reset-password.str +4 -18
  116. data/templates/sms-auth.str +6 -0
  117. data/templates/sms-code-field.str +6 -0
  118. data/templates/sms-confirm.str +7 -0
  119. data/templates/sms-disable.str +7 -0
  120. data/templates/sms-request.str +5 -0
  121. data/templates/sms-setup.str +12 -0
  122. data/templates/unlock-account-request.str +3 -7
  123. data/templates/unlock-account.str +4 -7
  124. data/templates/verify-account-resend.str +2 -2
  125. data/templates/verify-account.str +2 -6
  126. metadata +191 -29
  127. data/lib/roda/plugins/rodauth/base.rb +0 -428
  128. data/lib/roda/plugins/rodauth/change_login.rb +0 -48
  129. data/lib/roda/plugins/rodauth/change_password.rb +0 -42
  130. data/lib/roda/plugins/rodauth/close_account.rb +0 -42
  131. data/lib/roda/plugins/rodauth/create_account.rb +0 -92
  132. data/lib/roda/plugins/rodauth/lockout.rb +0 -292
  133. data/lib/roda/plugins/rodauth/login.rb +0 -81
  134. data/lib/roda/plugins/rodauth/logout.rb +0 -36
  135. data/lib/roda/plugins/rodauth/remember.rb +0 -226
  136. data/lib/roda/plugins/rodauth/reset_password.rb +0 -205
  137. data/lib/roda/plugins/rodauth/verify_account.rb +0 -228
@@ -1,428 +0,0 @@
1
- class Roda
2
- module RodaPlugins
3
- module Rodauth
4
- Base = Feature.define(:base) do
5
- auth_value_methods(
6
- :account_id,
7
- :account_model,
8
- :account_open_status_value,
9
- :account_password_hash_column,
10
- :account_status_id,
11
- :account_unverified_status_value,
12
- :default_redirect,
13
- :email_from,
14
- :email_subject_prefix,
15
- :login_column,
16
- :login_confirm_label,
17
- :login_confirm_param,
18
- :login_label,
19
- :login_param,
20
- :logins_do_not_match_message,
21
- :no_matching_login_message,
22
- :password_confirm_label,
23
- :password_confirm_param,
24
- :password_does_not_meet_requirements_message,
25
- :password_hash_column,
26
- :password_hash_cost,
27
- :password_hash_table,
28
- :password_label,
29
- :password_minimum_length,
30
- :password_param,
31
- :passwords_do_not_match_message,
32
- :prefix,
33
- :require_login_notice_message,
34
- :require_login_redirect,
35
- :session_key,
36
- :skip_status_checks?,
37
- :title_instance_variable
38
- )
39
-
40
- auth_methods(
41
- :account_from_login,
42
- :account_from_session,
43
- :account_id_value,
44
- :account_session_value,
45
- :after_close_account,
46
- :already_logged_in,
47
- :clear_session,
48
- :create_email,
49
- :email_to,
50
- :logged_in?,
51
- :login_errors_message,
52
- :login_required,
53
- :open_account?,
54
- :password_hash,
55
- :password_meets_requirements?,
56
- :random_key,
57
- :session_value,
58
- :set_error_flash,
59
- :set_notice_flash,
60
- :set_password,
61
- :set_redirect_error_flash,
62
- :set_title,
63
- :unverified_account_message,
64
- :update_session
65
- )
66
-
67
- attr_reader :scope
68
- attr_reader :account
69
-
70
- def initialize(scope)
71
- @scope = scope
72
- end
73
-
74
- def features
75
- self.class.features
76
- end
77
-
78
- def request
79
- scope.request
80
- end
81
-
82
- def response
83
- scope.response
84
- end
85
-
86
- def session
87
- scope.session
88
- end
89
-
90
- def flash
91
- scope.flash
92
- end
93
-
94
- # Overridable methods
95
-
96
- def account_id_value
97
- account.send(account_id)
98
- end
99
- alias account_session_value account_id_value
100
-
101
- def session_value
102
- session[session_key]
103
- end
104
-
105
- def account_status_id_value
106
- account.send(account_status_id)
107
- end
108
-
109
- def _account_from_login(login)
110
- @account = account_from_login(login)
111
- end
112
-
113
- def account_from_login(login)
114
- ds = account_model.where(login_column=>login)
115
- ds = ds.where(account_status_id=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks?
116
- ds.first
117
- end
118
-
119
- def open_account?
120
- skip_status_checks? || account_status_id_value == account_open_status_value
121
- end
122
-
123
- def unverified_account_message
124
- "unverified account, please verify account before logging in"
125
- end
126
-
127
- def update_session
128
- clear_session
129
- session[session_key] = account_session_value
130
- end
131
-
132
- def check_before(feature)
133
- meth = :"check_before_#{feature.feature_name}"
134
- if respond_to?(meth)
135
- send(meth)
136
- elsif feature.account_required?
137
- require_account
138
- elsif logged_in?
139
- already_logged_in
140
- end
141
- end
142
-
143
- def account_model
144
- ::Account
145
- end
146
-
147
- def db
148
- account_model.db
149
- end
150
-
151
- # If the account_password_hash_column is set, the password hash is verified in
152
- # ruby, it will not use a database function to do so, it will check the password
153
- # hash using bcrypt.
154
- def account_password_hash_column
155
- nil
156
- end
157
-
158
- def already_logged_in
159
- nil
160
- end
161
-
162
- def clear_session
163
- session.clear
164
- end
165
-
166
- def default_redirect
167
- '/'
168
- end
169
-
170
- def require_login_redirect
171
- "#{prefix}/login"
172
- end
173
-
174
- def require_login_notice_message
175
- "Please login to continue"
176
- end
177
-
178
- def prefix
179
- ''
180
- end
181
-
182
- def login_required
183
- set_notice_flash require_login_notice_message
184
- request.redirect require_login_redirect
185
- end
186
-
187
- def random_key
188
- require 'securerandom'
189
- if RUBY_VERSION >= '1.9'
190
- SecureRandom.urlsafe_base64(32)
191
- else
192
- # :nocov:
193
- SecureRandom.hex(32)
194
- # :nocov:
195
- end
196
- end
197
-
198
- def title_instance_variable
199
- nil
200
- end
201
-
202
- def set_title(title)
203
- if title_instance_variable
204
- scope.instance_variable_set(title_instance_variable, title)
205
- end
206
- end
207
-
208
- def set_error_flash(message)
209
- flash.now[:error] = message
210
- end
211
-
212
- def set_redirect_error_flash(message)
213
- flash[:error] = message
214
- end
215
-
216
- def set_notice_flash(message)
217
- flash[:notice] = message
218
- end
219
-
220
- def login_column
221
- :email
222
- end
223
-
224
- def password_hash_column
225
- :password_hash
226
- end
227
-
228
- def password_hash_table
229
- :account_password_hashes
230
- end
231
-
232
- def no_matching_login_message
233
- "no matching login"
234
- end
235
-
236
- def logged_in?
237
- session[session_key]
238
- end
239
-
240
- def require_login
241
- login_required unless logged_in?
242
- end
243
-
244
- def require_account
245
- require_login
246
- unless _account_from_session
247
- clear_session
248
- login_required
249
- end
250
- end
251
-
252
- def login_param
253
- 'login'
254
- end
255
-
256
- def login_confirm_param
257
- 'login-confirm'
258
- end
259
-
260
- def login_label
261
- 'Login'
262
- end
263
-
264
- def login_confirm_label
265
- "Confirm #{login_label}"
266
- end
267
-
268
- def password_label
269
- 'Password'
270
- end
271
-
272
- def password_confirm_label
273
- "Confirm #{password_label}"
274
- end
275
-
276
- def login_errors_message
277
- if errors = account.errors.on(login_column)
278
- errors.join(', ')
279
- end
280
- end
281
-
282
- def logins_do_not_match_message
283
- 'logins do not match'
284
- end
285
-
286
- def password_param
287
- 'password'
288
- end
289
-
290
- def password_confirm_param
291
- 'password-confirm'
292
- end
293
-
294
- def session_key
295
- :account_id
296
- end
297
-
298
- def account_id
299
- :id
300
- end
301
-
302
- def account_status_id
303
- :status_id
304
- end
305
-
306
- def passwords_do_not_match_message
307
- 'passwords do not match'
308
- end
309
-
310
- def password_does_not_meet_requirements_message
311
- "invalid password, does not meet requirements (minimum #{password_minimum_length} characters)"
312
- end
313
-
314
- def password_minimum_length
315
- 6
316
- end
317
-
318
- def password_meets_requirements?(password)
319
- password_minimum_length <= password.length
320
- end
321
-
322
- def account_unverified_status_value
323
- 1
324
- end
325
-
326
- def account_open_status_value
327
- 2
328
- end
329
-
330
- def account_initial_status_value
331
- account_open_status_value
332
- end
333
-
334
- def _account_from_session
335
- @account = account_from_session
336
- end
337
-
338
- def account_from_session
339
- ds = account_model.where(account_id=>scope.session[session_key])
340
- ds = ds.where(account_status_id=>account_open_status_value) unless skip_status_checks?
341
- ds.first
342
- end
343
-
344
- def password_hash_cost
345
- require 'bcrypt'
346
- if ENV['RACK_ENV'] == 'test'
347
- BCrypt::Engine::MIN_COST
348
- else
349
- # :nocov:
350
- BCrypt::Engine::DEFAULT_COST
351
- # :nocov:
352
- end
353
- end
354
-
355
- def password_hash(password)
356
- require 'bcrypt'
357
- BCrypt::Password.create(password, :cost=>password_hash_cost)
358
- end
359
-
360
- def set_password(password)
361
- hash = password_hash(password)
362
- if account_password_hash_column
363
- account.set(account_password_hash_column=>hash).save_changes(:raise_on_save_failure=>true)
364
- else
365
- if db[password_hash_table].where(account_id=>account_id_value).update(password_hash_column=>hash) == 0
366
- db[password_hash_table].insert(account_id=>account_id_value, password_hash_column=>hash)
367
- end
368
- end
369
- end
370
-
371
- def transaction(&block)
372
- db.transaction(&block)
373
- end
374
-
375
- def email_from
376
- "webmaster@#{request.host}"
377
- end
378
-
379
- def email_to
380
- account.email
381
- end
382
-
383
- def create_email(subject, body)
384
- require 'mail'
385
- m = Mail.new
386
- m.from = email_from
387
- m.to = email_to
388
- m.subject = "#{email_subject_prefix}#{subject}"
389
- m.body = body
390
- m
391
- end
392
-
393
- def email_subject_prefix
394
- nil
395
- end
396
-
397
- def view(page, title)
398
- set_title(title)
399
- _view(:view, page)
400
- end
401
-
402
- def render(page)
403
- _view(:render, page)
404
- end
405
-
406
- def skip_status_checks?
407
- false
408
- end
409
-
410
- def after_close_account
411
- end
412
-
413
- private
414
-
415
- def _view(meth, page)
416
- auth = self
417
- scope.instance_exec do
418
- template_opts = find_template(parse_template_opts(page, :locals=>{:rodauth=>auth}))
419
- unless File.file?(template_path(template_opts))
420
- template_opts[:path] = File.join(File.dirname(__FILE__), '../../../../templates', "#{page}.str")
421
- end
422
- send(meth, template_opts)
423
- end
424
- end
425
- end
426
- end
427
- end
428
- end
@@ -1,48 +0,0 @@
1
- class Roda
2
- module RodaPlugins
3
- module Rodauth
4
- ChangeLogin = Feature.define(:change_login) do
5
- route 'change-login'
6
- notice_flash 'Your login has been changed'
7
- error_flash 'There was an error changing your login'
8
- view 'change-login', 'Change Login'
9
- after
10
- additional_form_tags
11
- button 'Change Login'
12
- redirect
13
- require_account
14
-
15
- auth_methods :change_login
16
-
17
- get_block do |r, auth|
18
- auth.view('change-login', 'Change Login')
19
- end
20
-
21
- post_block do |r, auth|
22
- if r[auth.login_param] == r[auth.login_confirm_param]
23
- auth.transaction do
24
- if auth.change_login(r[auth.login_param].to_s)
25
- auth.after_change_login
26
- auth.set_notice_flash auth.change_login_notice_flash
27
- r.redirect(auth.change_login_redirect)
28
- else
29
- @login_error = auth.login_errors_message
30
- end
31
- end
32
- else
33
- @login_error = auth.logins_do_not_match_message
34
- end
35
-
36
- auth.set_error_flash auth.change_login_error_flash
37
- auth.change_login_view
38
- end
39
-
40
- def change_login(login)
41
- account.set(login_column=>login).save_changes(:raise_on_failure=>false)
42
- end
43
- end
44
- end
45
- end
46
- end
47
-
48
-