rodauth 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,108 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ AccountExpiration = Feature.define(:account_expiration) do
5
+ error_flash "You cannot log into this account as it has expired"
6
+ redirect
7
+ after
8
+
9
+ auth_value_method :account_activity_expired_column, :expired_at
10
+ auth_value_method :account_activity_id_column, :id
11
+ auth_value_method :account_activity_last_activity_column, :last_activity_at
12
+ auth_value_method :account_activity_last_login_column, :last_login_at
13
+ auth_value_method :account_activity_table, :account_activity_times
14
+ auth_value_method :expire_account_after, 180*86400
15
+ auth_value_method :expire_account_on_last_activity?, false
16
+
17
+ auth_methods(
18
+ :account_expired?,
19
+ :account_expired_at,
20
+ :last_account_activity_at,
21
+ :last_account_login_at,
22
+ :set_expired,
23
+ :update_last_activity,
24
+ :update_last_login
25
+ )
26
+
27
+ def last_account_activity_at
28
+ get_activity_timestamp(session_value, account_activity_last_activity_column)
29
+ end
30
+
31
+ def last_account_login_at
32
+ get_activity_timestamp(session_value, account_activity_last_login_column)
33
+ end
34
+
35
+ def account_expired_at
36
+ get_activity_timestamp(account_id, account_activity_expired_column)
37
+ end
38
+
39
+ def update_last_login
40
+ update_activity(account_id, account_activity_last_login_column, account_activity_last_activity_column)
41
+ end
42
+
43
+ def update_last_activity
44
+ if session_value
45
+ update_activity(session_value, account_activity_last_activity_column)
46
+ end
47
+ end
48
+
49
+ def set_expired
50
+ update_activity(account_id, account_activity_expired_column)
51
+ after_account_expiration
52
+ end
53
+
54
+ def account_expired?
55
+ columns = [account_activity_last_activity_column, account_activity_last_login_column, account_activity_expired_column]
56
+ last_activity, last_login, expired = account_activity_ds(account_id).get(columns)
57
+ return true if expired
58
+ timestamp = convert_timestamp(expire_account_on_last_activity? ? last_activity : last_login)
59
+ return false unless timestamp
60
+ timestamp < Time.now - expire_account_after
61
+ end
62
+
63
+ def check_account_expiration
64
+ if account_expired?
65
+ set_expired unless account_expired_at
66
+ set_redirect_error_flash account_expiration_error_flash
67
+ redirect account_expiration_redirect
68
+ end
69
+ update_last_login
70
+ end
71
+
72
+ private
73
+
74
+ def after_close_account
75
+ super if defined?(super)
76
+ account_activity_ds(account_id).delete
77
+ end
78
+
79
+ def update_session
80
+ check_account_expiration
81
+ super
82
+ end
83
+
84
+ def account_activity_ds(account_id)
85
+ db[account_activity_table].
86
+ where(account_activity_id_column=>account_id)
87
+ end
88
+
89
+ def get_activity_timestamp(account_id, column)
90
+ convert_timestamp(account_activity_ds(account_id).get(column))
91
+ end
92
+
93
+ def update_activity(account_id, *columns)
94
+ ds = account_activity_ds(account_id)
95
+ hash = {}
96
+ columns.each do |c|
97
+ hash[c] = Sequel::CURRENT_TIMESTAMP
98
+ end
99
+ if ds.update(hash) == 0
100
+ hash[account_activity_id_column] = account_id
101
+ hash[account_activity_last_activity_column] ||= Sequel::CURRENT_TIMESTAMP
102
+ hash[account_activity_last_login_column] ||= Sequel::CURRENT_TIMESTAMP
103
+ # It is safe to ignore uniqueness violations here, as a concurrent insert would also use current timestamps.
104
+ ignore_uniqueness_violation{ds.insert(hash)}
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,479 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ Base = Feature.define(:base) do
5
+ before 'rodauth'
6
+
7
+ error_flash "Please login to continue", 'require_login'
8
+
9
+ auth_value_method :account_id_column, :id
10
+ auth_value_method :account_open_status_value, 2
11
+ auth_value_method :account_password_hash_column, nil
12
+ auth_value_method :account_select, nil
13
+ auth_value_method :account_status_column, :status_id
14
+ auth_value_method :account_unverified_status_value, 1
15
+ auth_value_method :accounts_table, :accounts
16
+ auth_value_method :default_redirect, '/'
17
+ auth_value_method :invalid_password_message, "invalid password"
18
+ auth_value_method :login_column, :email
19
+ auth_value_method :password_hash_id_column, :id
20
+ auth_value_method :password_hash_column, :password_hash
21
+ auth_value_method :password_hash_table, :account_password_hashes
22
+ auth_value_method :no_matching_login_message, "no matching login"
23
+ auth_value_method :login_param, 'login'
24
+ auth_value_method :login_label, 'Login'
25
+ auth_value_method :password_label, 'Password'
26
+ auth_value_method :password_param, 'password'
27
+ auth_value_method :modifications_require_password?, true
28
+ auth_value_method :session_key, :account_id
29
+ auth_value_method :prefix, ''
30
+ auth_value_method :require_bcrypt?, true
31
+ auth_value_method :skip_status_checks?, true
32
+ auth_value_method :title_instance_variable, nil
33
+ auth_value_method :unverified_account_message, "unverified account, please verify account before logging in"
34
+
35
+ redirect(:require_login){"#{prefix}/login"}
36
+
37
+ auth_value_methods(
38
+ :db,
39
+ :require_login_redirect,
40
+ :set_deadline_values?,
41
+ :use_date_arithmetic?,
42
+ :use_database_authentication_functions?
43
+ )
44
+
45
+ auth_methods(
46
+ :account_id,
47
+ :account_session_value,
48
+ :already_logged_in,
49
+ :authenticated?,
50
+ :clear_session,
51
+ :csrf_tag,
52
+ :function_name,
53
+ :logged_in?,
54
+ :login_required,
55
+ :open_account?,
56
+ :password_match?,
57
+ :random_key,
58
+ :redirect,
59
+ :session_value,
60
+ :set_error_flash,
61
+ :set_notice_flash,
62
+ :set_notice_now_flash,
63
+ :set_redirect_error_flash,
64
+ :set_title,
65
+ :unverified_account_message,
66
+ :update_session
67
+ )
68
+
69
+ auth_private_methods(
70
+ :account_from_login,
71
+ :account_from_session
72
+ )
73
+
74
+ configuration_module_eval do
75
+ def auth_class_eval(&block)
76
+ auth.class_eval(&block)
77
+ end
78
+
79
+ def account_model(model)
80
+ warn "account_model is deprecated, use db and accounts_table settings"
81
+ db model.db
82
+ accounts_table model.table_name
83
+ account_select model.dataset.opts[:select]
84
+ end
85
+ end
86
+
87
+ attr_reader :scope
88
+ attr_reader :account
89
+
90
+ def initialize(scope)
91
+ @scope = scope
92
+ end
93
+
94
+ def features
95
+ self.class.features
96
+ end
97
+
98
+ def request
99
+ scope.request
100
+ end
101
+
102
+ def response
103
+ scope.response
104
+ end
105
+
106
+ def session
107
+ scope.session
108
+ end
109
+
110
+ def flash
111
+ scope.flash
112
+ end
113
+
114
+ def route!
115
+ if meth = self.class.route_hash[request.remaining_path]
116
+ send(meth)
117
+ end
118
+
119
+ nil
120
+ end
121
+
122
+ def set_field_error(field, error)
123
+ (@field_errors ||= {})[field] = error
124
+ end
125
+
126
+ def field_error(field)
127
+ return nil unless @field_errors
128
+ @field_errors[field]
129
+ end
130
+
131
+ def account_id
132
+ account[account_id_column]
133
+ end
134
+ alias account_session_value account_id
135
+
136
+ def session_value
137
+ session[session_key]
138
+ end
139
+ alias logged_in? session_value
140
+
141
+ def account_from_login(login)
142
+ @account = _account_from_login(login)
143
+ end
144
+
145
+ def open_account?
146
+ skip_status_checks? || account[account_status_column] == account_open_status_value
147
+ end
148
+
149
+ def db
150
+ Sequel::DATABASES.first
151
+ end
152
+
153
+ # If the account_password_hash_column is set, the password hash is verified in
154
+ # ruby, it will not use a database function to do so, it will check the password
155
+ # hash using bcrypt.
156
+ def account_password_hash_column
157
+ nil
158
+ end
159
+
160
+ def check_already_logged_in
161
+ already_logged_in if logged_in?
162
+ end
163
+
164
+ def already_logged_in
165
+ nil
166
+ end
167
+
168
+ def clear_session
169
+ session.clear
170
+ end
171
+
172
+ def login_required
173
+ set_redirect_error_flash require_login_error_flash
174
+ redirect require_login_redirect
175
+ end
176
+
177
+ def set_title(title)
178
+ if title_instance_variable
179
+ scope.instance_variable_set(title_instance_variable, title)
180
+ end
181
+ end
182
+
183
+ def set_error_flash(message)
184
+ flash.now[:error] = message
185
+ end
186
+
187
+ def set_redirect_error_flash(message)
188
+ flash[:error] = message
189
+ end
190
+
191
+ def set_notice_flash(message)
192
+ flash[:notice] = message
193
+ end
194
+
195
+ def set_notice_now_flash(message)
196
+ flash.now[:notice] = message
197
+ end
198
+
199
+ def require_login
200
+ login_required unless logged_in?
201
+ end
202
+
203
+ def authenticated?
204
+ logged_in?
205
+ end
206
+
207
+ def require_authentication
208
+ require_login
209
+ end
210
+
211
+ def account_initial_status_value
212
+ account_open_status_value
213
+ end
214
+
215
+ def account_from_session
216
+ @account = _account_from_session
217
+ end
218
+
219
+ def csrf_tag
220
+ scope.csrf_tag if scope.respond_to?(:csrf_tag)
221
+ end
222
+
223
+ def button(value, opts={})
224
+ opts = {:locals=>{:value=>value, :opts=>opts}}
225
+ opts[:path] = template_path('button')
226
+ scope.render(opts)
227
+ end
228
+
229
+ def view(page, title)
230
+ set_title(title)
231
+ _view(:view, page)
232
+ end
233
+
234
+ def render(page)
235
+ _view(:render, page)
236
+ end
237
+
238
+ def post_configure
239
+ require 'bcrypt' if require_bcrypt?
240
+ db.extension :date_arithmetic if use_date_arithmetic?
241
+ route_hash= {}
242
+ self.class.routes.each do |meth|
243
+ route_hash["/#{send("#{meth.to_s.sub(/\Ahandle_/, '')}_route")}"] = meth
244
+ end
245
+ self.class.route_hash = route_hash.freeze
246
+ end
247
+
248
+ def password_match?(password)
249
+ if account_password_hash_column
250
+ BCrypt::Password.new(account[account_password_hash_column]) == password
251
+ elsif use_database_authentication_functions?
252
+ id = account_id
253
+ if salt = db.get(Sequel.function(function_name(:rodauth_get_salt), id))
254
+ hash = BCrypt::Engine.hash_secret(password, salt)
255
+ db.get(Sequel.function(function_name(:rodauth_valid_password_hash), id, hash))
256
+ end
257
+ else
258
+ # :nocov:
259
+ if hash = password_hash_ds.get(password_hash_column)
260
+ BCrypt::Password.new(hash) == password
261
+ end
262
+ # :nocov:
263
+ end
264
+ end
265
+
266
+ private
267
+
268
+ def update_session
269
+ clear_session
270
+ session[session_key] = account_session_value
271
+ end
272
+
273
+ # Return a string for the parameter name. This will be an empty
274
+ # string if the parameter doesn't exist.
275
+ def param(key)
276
+ param_or_nil(key).to_s
277
+ end
278
+
279
+ # Return a string for the parameter name, or nil if there is no
280
+ # parameter with that name.
281
+ def param_or_nil(key)
282
+ value = request.params[key]
283
+ value.to_s unless value.nil?
284
+ end
285
+
286
+ def redirect(path)
287
+ request.redirect(path)
288
+ end
289
+
290
+ def transaction(opts={}, &block)
291
+ db.transaction(opts, &block)
292
+ end
293
+
294
+ if RUBY_VERSION >= '1.9'
295
+ def random_key
296
+ SecureRandom.urlsafe_base64(32)
297
+ end
298
+ else
299
+ # :nocov:
300
+ def random_key
301
+ SecureRandom.hex(32)
302
+ end
303
+ # :nocov:
304
+ end
305
+
306
+ def timing_safe_eql?(provided, actual)
307
+ provided = provided.to_s
308
+ Rack::Utils.secure_compare(provided.ljust(actual.length), actual) && provided.length == actual.length
309
+ end
310
+
311
+ def require_account
312
+ require_authentication
313
+ require_account_session
314
+ end
315
+
316
+ def require_account_session
317
+ unless account_from_session
318
+ clear_session
319
+ login_required
320
+ end
321
+ end
322
+
323
+ def catch_error(&block)
324
+ catch(:rodauth_error, &block)
325
+ end
326
+
327
+ def throw_error(field, error)
328
+ set_field_error(field, error)
329
+ throw :rodauth_error
330
+ end
331
+
332
+ def use_date_arithmetic?
333
+ set_deadline_values?
334
+ end
335
+
336
+ def set_deadline_values?
337
+ db.database_type == :mysql
338
+ end
339
+
340
+ def use_database_authentication_functions?
341
+ case db.database_type
342
+ when :postgres, :mysql, :mssql
343
+ true
344
+ else
345
+ # :nocov:
346
+ false
347
+ # :nocov:
348
+ end
349
+ end
350
+
351
+ def function_name(name)
352
+ if db.database_type == :mssql
353
+ # :nocov:
354
+ "dbo.#{name}"
355
+ # :nocov:
356
+ else
357
+ name
358
+ end
359
+ end
360
+
361
+ def _account_from_login(login)
362
+ ds = db[accounts_table].where(login_column=>login)
363
+ ds = ds.select(*account_select) if account_select
364
+ ds = ds.where(account_status_column=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks?
365
+ ds.first
366
+ end
367
+
368
+ def _account_from_session
369
+ ds = account_ds(session_value)
370
+ ds = ds.where(account_session_status_filter) unless skip_status_checks?
371
+ ds.first
372
+ end
373
+
374
+ def account_session_status_filter
375
+ {account_status_column=>account_open_status_value}
376
+ end
377
+
378
+ def template_path(page)
379
+ File.join(File.dirname(__FILE__), '../../../templates', "#{page}.str")
380
+ end
381
+
382
+ def account_ds(id=account_id)
383
+ raise ArgumentError, "invalid account id passed to account_ds" unless id
384
+ ds = db[accounts_table].where(account_id_column=>id)
385
+ ds = ds.select(*account_select) if account_select
386
+ ds
387
+ end
388
+
389
+ def password_hash_ds
390
+ db[password_hash_table].where(password_hash_id_column=>account_id)
391
+ end
392
+
393
+ # This is needed for jdbc/sqlite, which returns timestamp columns as strings
394
+ def convert_timestamp(timestamp)
395
+ timestamp = db.to_application_timestamp(timestamp) if timestamp.is_a?(String)
396
+ timestamp
397
+ end
398
+
399
+ # This is used to avoid race conditions when using the pattern of inserting when
400
+ # an update affects no rows. In such cases, if a row is inserted between the
401
+ # update and the insert, the insert will fail with a uniqueness error, but
402
+ # retrying will work. It is possible for it to fail again, but only if the row
403
+ # is deleted before the update and readded before the insert, which is very
404
+ # unlikely to happen. In such cases, raising an exception is acceptable.
405
+ def retry_on_uniqueness_violation(&block)
406
+ if raises_uniqueness_violation?(&block)
407
+ yield
408
+ end
409
+ end
410
+
411
+ # In cases where retrying on uniqueness violations cannot work, this will detect
412
+ # whether a uniqueness violation is raised by the block and return the exception if so.
413
+ # This method should be used if you don't care about the exception itself.
414
+ def raises_uniqueness_violation?(&block)
415
+ transaction(:savepoint=>:only, &block)
416
+ false
417
+ rescue unique_constraint_violation_class => e
418
+ e
419
+ end
420
+
421
+ # Work around jdbc/sqlite issue where it only raises ConstraintViolation and not
422
+ # UniqueConstraintViolation.
423
+ def unique_constraint_violation_class
424
+ if db.adapter_scheme == :jdbc && db.database_type == :sqlite
425
+ # :nocov:
426
+ Sequel::ConstraintViolation
427
+ # :nocov:
428
+ else
429
+ Sequel::UniqueConstraintViolation
430
+ end
431
+ end
432
+
433
+ # If you would like to operate/reraise the exception, this alias makes more sense.
434
+ alias raised_uniqueness_violation raises_uniqueness_violation?
435
+
436
+ # If you just want to ignore uniqueness violations, this alias makes more sense.
437
+ alias ignore_uniqueness_violation raises_uniqueness_violation?
438
+
439
+ # This is needed on MySQL, which doesn't support non constant defaults other than
440
+ # CURRENT_TIMESTAMP.
441
+ def set_deadline_value(hash, column, interval)
442
+ if set_deadline_values?
443
+ # :nocov:
444
+ hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval)
445
+ # :nocov:
446
+ end
447
+ end
448
+
449
+ def set_session_value(key, value)
450
+ session[key] = value
451
+ end
452
+
453
+ def update_hash_ds(hash, ds, values)
454
+ num = ds.update(values)
455
+ if num == 1
456
+ values.each do |k, v|
457
+ account[k] = v == Sequel::CURRENT_TIMESTAMP ? Time.now : v
458
+ end
459
+ end
460
+ num
461
+ end
462
+
463
+ def update_account(values, ds=account_ds)
464
+ update_hash_ds(account, ds, values)
465
+ end
466
+
467
+ def _view(meth, page)
468
+ auth = self
469
+ auth_template_path = template_path(page)
470
+ scope.instance_exec do
471
+ template_opts = find_template(parse_template_opts(page, :locals=>{:rodauth=>auth}))
472
+ unless File.file?(template_path(template_opts))
473
+ template_opts[:path] = auth_template_path
474
+ end
475
+ send(meth, template_opts)
476
+ end
477
+ end
478
+ end
479
+ end