rodauth 1.22.0 → 2.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +190 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +210 -80
  5. data/doc/account_expiration.rdoc +12 -26
  6. data/doc/active_sessions.rdoc +49 -0
  7. data/doc/audit_logging.rdoc +44 -0
  8. data/doc/base.rdoc +75 -128
  9. data/doc/change_login.rdoc +7 -14
  10. data/doc/change_password.rdoc +9 -13
  11. data/doc/change_password_notify.rdoc +2 -2
  12. data/doc/close_account.rdoc +9 -16
  13. data/doc/confirm_password.rdoc +12 -5
  14. data/doc/create_account.rdoc +11 -22
  15. data/doc/disallow_password_reuse.rdoc +6 -13
  16. data/doc/email_auth.rdoc +15 -14
  17. data/doc/email_base.rdoc +6 -15
  18. data/doc/guides/admin_activation.rdoc +46 -0
  19. data/doc/guides/already_authenticated.rdoc +10 -0
  20. data/doc/guides/alternative_login.rdoc +46 -0
  21. data/doc/guides/create_account_programmatically.rdoc +38 -0
  22. data/doc/guides/delay_password.rdoc +25 -0
  23. data/doc/guides/email_only.rdoc +16 -0
  24. data/doc/guides/i18n.rdoc +26 -0
  25. data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
  26. data/doc/guides/links.rdoc +12 -0
  27. data/doc/guides/login_return.rdoc +37 -0
  28. data/doc/guides/password_column.rdoc +25 -0
  29. data/doc/guides/password_confirmation.rdoc +37 -0
  30. data/doc/guides/password_requirements.rdoc +30 -0
  31. data/doc/guides/paths.rdoc +36 -0
  32. data/doc/guides/query_params.rdoc +9 -0
  33. data/doc/guides/redirects.rdoc +17 -0
  34. data/doc/guides/registration_field.rdoc +68 -0
  35. data/doc/guides/require_mfa.rdoc +30 -0
  36. data/doc/guides/reset_password_autologin.rdoc +21 -0
  37. data/doc/guides/status_column.rdoc +28 -0
  38. data/doc/guides/totp_or_recovery.rdoc +16 -0
  39. data/doc/http_basic_auth.rdoc +10 -1
  40. data/doc/jwt.rdoc +22 -22
  41. data/doc/jwt_cors.rdoc +2 -3
  42. data/doc/jwt_refresh.rdoc +23 -8
  43. data/doc/lockout.rdoc +17 -15
  44. data/doc/login.rdoc +17 -2
  45. data/doc/login_password_requirements_base.rdoc +18 -37
  46. data/doc/logout.rdoc +2 -2
  47. data/doc/otp.rdoc +25 -19
  48. data/doc/password_complexity.rdoc +10 -26
  49. data/doc/password_expiration.rdoc +11 -25
  50. data/doc/password_grace_period.rdoc +16 -2
  51. data/doc/recovery_codes.rdoc +18 -12
  52. data/doc/release_notes/1.23.0.txt +32 -0
  53. data/doc/release_notes/2.0.0.txt +361 -0
  54. data/doc/release_notes/2.1.0.txt +31 -0
  55. data/doc/release_notes/2.2.0.txt +39 -0
  56. data/doc/release_notes/2.3.0.txt +37 -0
  57. data/doc/remember.rdoc +40 -64
  58. data/doc/reset_password.rdoc +12 -9
  59. data/doc/session_expiration.rdoc +1 -0
  60. data/doc/single_session.rdoc +16 -25
  61. data/doc/sms_codes.rdoc +24 -14
  62. data/doc/two_factor_base.rdoc +60 -22
  63. data/doc/verify_account.rdoc +14 -12
  64. data/doc/verify_account_grace_period.rdoc +6 -2
  65. data/doc/verify_login_change.rdoc +9 -8
  66. data/doc/webauthn.rdoc +115 -0
  67. data/doc/webauthn_login.rdoc +15 -0
  68. data/doc/webauthn_verify_account.rdoc +9 -0
  69. data/javascript/webauthn_auth.js +45 -0
  70. data/javascript/webauthn_setup.js +35 -0
  71. data/lib/roda/plugins/rodauth.rb +1 -1
  72. data/lib/rodauth.rb +36 -28
  73. data/lib/rodauth/features/account_expiration.rb +5 -5
  74. data/lib/rodauth/features/active_sessions.rb +158 -0
  75. data/lib/rodauth/features/audit_logging.rb +98 -0
  76. data/lib/rodauth/features/base.rb +144 -43
  77. data/lib/rodauth/features/change_password_notify.rb +2 -2
  78. data/lib/rodauth/features/close_account.rb +8 -6
  79. data/lib/rodauth/features/confirm_password.rb +40 -2
  80. data/lib/rodauth/features/create_account.rb +8 -13
  81. data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
  82. data/lib/rodauth/features/disallow_password_reuse.rb +1 -1
  83. data/lib/rodauth/features/email_auth.rb +31 -30
  84. data/lib/rodauth/features/email_base.rb +9 -4
  85. data/lib/rodauth/features/http_basic_auth.rb +55 -35
  86. data/lib/rodauth/features/jwt.rb +63 -16
  87. data/lib/rodauth/features/jwt_cors.rb +15 -15
  88. data/lib/rodauth/features/jwt_refresh.rb +42 -13
  89. data/lib/rodauth/features/lockout.rb +12 -14
  90. data/lib/rodauth/features/login.rb +64 -15
  91. data/lib/rodauth/features/login_password_requirements_base.rb +13 -8
  92. data/lib/rodauth/features/otp.rb +77 -80
  93. data/lib/rodauth/features/password_complexity.rb +8 -13
  94. data/lib/rodauth/features/password_expiration.rb +2 -2
  95. data/lib/rodauth/features/password_grace_period.rb +17 -10
  96. data/lib/rodauth/features/recovery_codes.rb +49 -53
  97. data/lib/rodauth/features/remember.rb +11 -27
  98. data/lib/rodauth/features/reset_password.rb +26 -26
  99. data/lib/rodauth/features/session_expiration.rb +7 -10
  100. data/lib/rodauth/features/single_session.rb +8 -6
  101. data/lib/rodauth/features/sms_codes.rb +62 -72
  102. data/lib/rodauth/features/two_factor_base.rb +134 -30
  103. data/lib/rodauth/features/verify_account.rb +29 -21
  104. data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
  105. data/lib/rodauth/features/verify_login_change.rb +12 -11
  106. data/lib/rodauth/features/webauthn.rb +505 -0
  107. data/lib/rodauth/features/webauthn_login.rb +70 -0
  108. data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
  109. data/lib/rodauth/migrations.rb +16 -5
  110. data/lib/rodauth/version.rb +2 -2
  111. data/templates/button.str +1 -3
  112. data/templates/change-login.str +1 -2
  113. data/templates/change-password.str +3 -5
  114. data/templates/close-account.str +2 -2
  115. data/templates/confirm-password.str +1 -1
  116. data/templates/create-account.str +1 -1
  117. data/templates/email-auth-request-form.str +2 -3
  118. data/templates/email-auth.str +1 -1
  119. data/templates/global-logout-field.str +6 -0
  120. data/templates/login-confirm-field.str +2 -4
  121. data/templates/login-display.str +3 -2
  122. data/templates/login-field.str +2 -4
  123. data/templates/login-form-footer.str +6 -0
  124. data/templates/login-form.str +7 -0
  125. data/templates/login.str +1 -9
  126. data/templates/logout.str +1 -1
  127. data/templates/multi-phase-login.str +3 -0
  128. data/templates/otp-auth-code-field.str +5 -3
  129. data/templates/otp-auth.str +1 -1
  130. data/templates/otp-disable.str +1 -1
  131. data/templates/otp-setup.str +3 -3
  132. data/templates/password-confirm-field.str +2 -4
  133. data/templates/password-field.str +2 -4
  134. data/templates/recovery-auth.str +3 -6
  135. data/templates/recovery-codes.str +1 -1
  136. data/templates/remember.str +15 -20
  137. data/templates/reset-password-request.str +3 -3
  138. data/templates/reset-password.str +1 -2
  139. data/templates/sms-auth.str +1 -1
  140. data/templates/sms-code-field.str +5 -3
  141. data/templates/sms-confirm.str +1 -2
  142. data/templates/sms-disable.str +1 -2
  143. data/templates/sms-request.str +1 -1
  144. data/templates/sms-setup.str +6 -4
  145. data/templates/two-factor-auth.str +5 -0
  146. data/templates/two-factor-disable.str +6 -0
  147. data/templates/two-factor-manage.str +16 -0
  148. data/templates/unlock-account-request.str +4 -4
  149. data/templates/unlock-account.str +1 -1
  150. data/templates/verify-account-resend.str +3 -3
  151. data/templates/verify-account.str +1 -2
  152. data/templates/verify-login-change.str +1 -1
  153. data/templates/webauthn-auth.str +11 -0
  154. data/templates/webauthn-remove.str +14 -0
  155. data/templates/webauthn-setup.str +12 -0
  156. metadata +94 -54
  157. data/Rakefile +0 -179
  158. data/doc/verify_change_login.rdoc +0 -11
  159. data/lib/rodauth/features/verify_change_login.rb +0 -20
  160. data/spec/account_expiration_spec.rb +0 -225
  161. data/spec/all.rb +0 -1
  162. data/spec/change_login_spec.rb +0 -156
  163. data/spec/change_password_notify_spec.rb +0 -33
  164. data/spec/change_password_spec.rb +0 -202
  165. data/spec/close_account_spec.rb +0 -162
  166. data/spec/confirm_password_spec.rb +0 -70
  167. data/spec/create_account_spec.rb +0 -127
  168. data/spec/disallow_common_passwords_spec.rb +0 -93
  169. data/spec/disallow_password_reuse_spec.rb +0 -179
  170. data/spec/email_auth_spec.rb +0 -285
  171. data/spec/http_basic_auth_spec.rb +0 -143
  172. data/spec/jwt_cors_spec.rb +0 -57
  173. data/spec/jwt_refresh_spec.rb +0 -256
  174. data/spec/jwt_spec.rb +0 -235
  175. data/spec/lockout_spec.rb +0 -250
  176. data/spec/login_spec.rb +0 -328
  177. data/spec/migrate/001_tables.rb +0 -184
  178. data/spec/migrate/002_account_password_hash_column.rb +0 -11
  179. data/spec/migrate_password/001_tables.rb +0 -73
  180. data/spec/migrate_travis/001_tables.rb +0 -141
  181. data/spec/password_complexity_spec.rb +0 -109
  182. data/spec/password_expiration_spec.rb +0 -244
  183. data/spec/password_grace_period_spec.rb +0 -93
  184. data/spec/remember_spec.rb +0 -451
  185. data/spec/reset_password_spec.rb +0 -229
  186. data/spec/rodauth_spec.rb +0 -343
  187. data/spec/session_expiration_spec.rb +0 -58
  188. data/spec/single_session_spec.rb +0 -127
  189. data/spec/spec_helper.rb +0 -327
  190. data/spec/two_factor_spec.rb +0 -1462
  191. data/spec/update_password_hash_spec.rb +0 -40
  192. data/spec/verify_account_grace_period_spec.rb +0 -171
  193. data/spec/verify_account_spec.rb +0 -240
  194. data/spec/verify_change_login_spec.rb +0 -46
  195. data/spec/verify_login_change_spec.rb +0 -232
  196. data/spec/views/layout-other.str +0 -11
  197. data/spec/views/layout.str +0 -11
  198. data/spec/views/login.str +0 -21
@@ -1,328 +0,0 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
2
-
3
- describe 'Rodauth login feature' do
4
- it "should handle logins and logouts" do
5
- rodauth{enable :login, :logout}
6
- roda do |r|
7
- r.rodauth
8
- next unless rodauth.logged_in?
9
- r.root{view :content=>"Logged In"}
10
- end
11
-
12
- visit '/login'
13
- page.title.must_equal 'Login'
14
-
15
- login(:login=>'foo@example2.com', :visit=>false)
16
- page.find('#error_flash').text.must_equal 'There was an error logging in'
17
- page.html.must_include("no matching login")
18
- page.all('[type=text]').first.value.must_equal 'foo@example2.com'
19
-
20
- login(:pass=>'012345678', :visit=>false)
21
- page.find('#error_flash').text.must_equal 'There was an error logging in'
22
- page.html.must_include("invalid password")
23
-
24
- fill_in 'Password', :with=>'0123456789'
25
- click_button 'Login'
26
- page.current_path.must_equal '/'
27
- page.find('#notice_flash').text.must_equal 'You have been logged in'
28
- page.html.must_include("Logged In")
29
-
30
- visit '/logout'
31
- page.title.must_equal 'Logout'
32
-
33
- click_button 'Logout'
34
- page.find('#notice_flash').text.must_equal 'You have been logged out'
35
- page.current_path.must_equal '/login'
36
- end
37
-
38
- it "should handle multi phase login (email first, then password)" do
39
- rodauth do
40
- enable :login, :logout
41
- use_multi_phase_login? true
42
- login_input_type 'email'
43
- input_field_label_suffix ' (Required)'
44
- input_field_error_class ' bad-input'
45
- input_field_error_message_class 'err-msg'
46
- mark_input_fields_as_required? true
47
- field_attributes do |field|
48
- if field == 'login'
49
- 'custom_field="custom_value"'
50
- else
51
- super(field)
52
- end
53
- end
54
- field_error_attributes do |field|
55
- if field == 'login'
56
- 'custom_error_field="custom_error_value"'
57
- else
58
- super(field)
59
- end
60
- end
61
- formatted_field_error do |field, error|
62
- if field == 'login'
63
- super(field, error)
64
- else
65
- "<span class='err-msg2'>1#{error}2</span>"
66
- end
67
- end
68
- end
69
- roda do |r|
70
- r.rodauth
71
- next unless rodauth.logged_in?
72
- r.root{view :content=>"Logged In"}
73
- end
74
-
75
- visit '/login'
76
- page.title.must_equal 'Login'
77
-
78
- page.find('[custom_field=custom_value]').value.must_equal ''
79
- page.all('[custom_error_field=custom_error_value]').must_be_empty
80
- page.all('input[type=password]').must_be_empty
81
- fill_in 'Login (Required)', :with=>'foo2@example.com'
82
- click_button 'Login'
83
- page.find('#error_flash').text.must_equal 'There was an error logging in'
84
- page.find('[custom_field=custom_value]').value.must_equal 'foo2@example.com'
85
- page.find('[custom_error_field=custom_error_value]').value.must_equal 'foo2@example.com'
86
- page.find('[type=email]').value.must_equal 'foo2@example.com'
87
- page.find('.bad-input').value.must_equal 'foo2@example.com'
88
- page.find('.err-msg').text.must_equal 'no matching login'
89
-
90
- page.all('input[type=password]').must_be_empty
91
- fill_in 'Login (Required)', :with=>'foo@example.com'
92
- click_button 'Login'
93
- page.find('#notice_flash').text.must_equal 'Login recognized, please enter your password'
94
-
95
- page.all('[custom_field=custom_value]').must_be_empty
96
- page.all('[custom_error_field=custom_error_value]').must_be_empty
97
- page.all('[aria-invalid=true]').must_be_empty
98
- page.all('[aria-describedby]').must_be_empty
99
- page.find('[required=required]').value.to_s.must_equal ''
100
- page.all('input[type=text]').must_be_empty
101
- fill_in 'Password (Required)', :with=>'012345678'
102
- click_button 'Login'
103
- page.find('#error_flash').text.must_equal 'There was an error logging in'
104
- page.find('[aria-invalid=true]').value.to_s.must_equal ''
105
- page.find('[aria-describedby=password_error_message]').value.to_s.must_equal ''
106
- page.all('[custom_error_field=custom_error_value]').must_be_empty
107
- page.find('.err-msg2').text.must_equal '1invalid password2'
108
-
109
- page.all('input[type=text]').must_be_empty
110
- fill_in 'Password (Required)', :with=>'0123456789'
111
- click_button 'Login'
112
- page.current_path.must_equal '/'
113
- page.find('#notice_flash').text.must_equal 'You have been logged in'
114
- page.html.must_include("Logged In")
115
-
116
- visit '/logout'
117
- page.title.must_equal 'Logout'
118
-
119
- click_button 'Logout'
120
- page.find('#notice_flash').text.must_equal 'You have been logged out'
121
- page.current_path.must_equal '/login'
122
- end
123
-
124
- it "should not allow login to unverified account" do
125
- rodauth do
126
- enable :login
127
- skip_status_checks? false
128
- end
129
- roda do |r|
130
- r.rodauth
131
- next unless rodauth.logged_in?
132
- r.root{view :content=>"Logged In"}
133
- end
134
-
135
- DB[:accounts].update(:status_id=>1)
136
- login
137
- page.find('#error_flash').text.must_equal 'There was an error logging in'
138
- page.html.must_include("unverified account, please verify account before logging in")
139
- end
140
-
141
- it "should handle overriding login action" do
142
- rodauth do
143
- enable :login
144
- end
145
- roda do |r|
146
- r.post 'login' do
147
- if r.params['login'] == 'apple' && r.params['password'] == 'banana'
148
- session['user_id'] = 'pear'
149
- r.redirect '/'
150
- end
151
- r.redirect '/login'
152
- end
153
- r.rodauth
154
- next unless session['user_id'] == 'pear'
155
- r.root{"Logged In"}
156
- end
157
-
158
- login(:login=>'appl', :pass=>'banana')
159
- page.html.wont_match(/Logged In/)
160
-
161
- login(:login=>'apple', :pass=>'banan', :visit=>false)
162
- page.html.wont_match(/Logged In/)
163
-
164
- login(:login=>'apple', :pass=>'banana', :visit=>false)
165
- page.current_path.must_equal '/'
166
- page.html.must_include("Logged In")
167
- end
168
-
169
- it "should handle overriding some login attributes" do
170
- rodauth do
171
- enable :login
172
- account_from_login do |login|
173
- DB[:accounts].first if login == 'apple'
174
- end
175
- password_match? do |password|
176
- password == 'banana'
177
- end
178
- update_session do
179
- session['user_id'] = 'pear'
180
- end
181
- no_matching_login_message "no user"
182
- invalid_password_message "bad password"
183
- end
184
- roda do |r|
185
- r.rodauth
186
- next unless session['user_id'] == 'pear'
187
- r.root{"Logged In"}
188
- end
189
-
190
- login(:login=>'appl', :pass=>'banana')
191
- page.html.must_include("no user")
192
-
193
- login(:login=>'apple', :pass=>'banan', :visit=>false)
194
- page.html.must_include("bad password")
195
-
196
- fill_in 'Password', :with=>'banana'
197
- click_button 'Login'
198
- page.current_path.must_equal '/'
199
- page.html.must_include("Logged In")
200
- end
201
-
202
- it "should handle a prefix and some other login options" do
203
- rodauth do
204
- enable :login, :logout
205
- prefix '/auth'
206
- session_key 'login_email'
207
- account_from_session{DB[:accounts].first(:email=>session_value)}
208
- account_session_value{account[:email]}
209
- login_param{param('lp')}
210
- login_additional_form_tags "<input type='hidden' name='lp' value='l' />"
211
- password_param 'p'
212
- login_redirect{"/foo/#{account[:email]}"}
213
- logout_redirect '/auth/lin'
214
- login_route 'lin'
215
- logout_route 'lout'
216
- end
217
- no_freeze!
218
- roda do |r|
219
- r.on 'auth' do
220
- r.rodauth
221
- end
222
- next unless session['login_email'] =~ /example/
223
- r.get('foo', :email){|e| "Logged In: #{e}"}
224
- end
225
- app.plugin :render, :views=>'spec/views', :engine=>'str'
226
-
227
- visit '/auth/lin?lp=l'
228
-
229
- login(:login=>'foo@example2.com', :visit=>false)
230
- page.html.must_include("no matching login")
231
-
232
- login(:pass=>'012345678', :visit=>false)
233
- page.html.must_include("invalid password")
234
-
235
- login(:visit=>false)
236
- page.current_path.must_equal '/foo/foo@example.com'
237
- page.html.must_include("Logged In: foo@example.com")
238
-
239
- visit '/auth/lout'
240
- click_button 'Logout'
241
- page.current_path.must_equal '/auth/lin'
242
- end
243
-
244
- it "should use correct redirect paths when using prefix" do
245
- rodauth do
246
- enable :login, :logout
247
- prefix '/auth'
248
- end
249
- roda do |r|
250
- r.on 'auth' do
251
- r.rodauth
252
- rodauth.require_login
253
- end
254
- rodauth.send("#{r.remaining_path[1..-1]}_redirect")
255
- end
256
-
257
- visit '/login'
258
- page.html.must_equal '/'
259
- visit '/logout'
260
- page.html.must_equal '/auth/login'
261
- visit '/require_login'
262
- page.html.must_equal '/auth/login'
263
-
264
- visit '/auth'
265
- page.current_path.must_equal '/auth/login'
266
- end
267
-
268
- it "should login and logout via jwt" do
269
- rodauth do
270
- enable :login, :logout
271
- json_response_custom_error_status? false
272
- jwt_secret{proc{super()}.must_raise ArgumentError; "1"}
273
- end
274
- roda(:jwt) do |r|
275
- r.rodauth
276
- response['Content-Type'] = 'application/json'
277
- rodauth.logged_in? ? '1' : '2'
278
- end
279
-
280
- json_request.must_equal [200, 2]
281
-
282
- res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
283
- res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
284
-
285
- res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
286
- res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
287
-
288
- json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
289
- json_request.must_equal [200, 1]
290
-
291
- json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
292
- json_request.must_equal [200, 2]
293
- end
294
-
295
- it "should login and logout via jwt with custom error statuses" do
296
- rodauth do
297
- enable :login, :logout
298
- end
299
- roda(:jwt) do |r|
300
- r.rodauth
301
- response['Content-Type'] = 'application/json'
302
- r.post('foo') do
303
- rodauth.require_login
304
- '3'
305
- end
306
- rodauth.logged_in? ? '1' : '2'
307
- end
308
-
309
- json_request.must_equal [200, 2]
310
-
311
- res = json_request("/foo")
312
- res.must_equal [401, {"error"=>"Please login to continue"}]
313
-
314
- res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
315
- res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
316
-
317
- res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
318
- res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
319
-
320
- json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
321
- json_request.must_equal [200, 1]
322
-
323
- res = json_request("/foo").must_equal [200, 3]
324
-
325
- json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
326
- json_request.must_equal [200, 2]
327
- end
328
- end
@@ -1,184 +0,0 @@
1
- Sequel.migration do
2
- up do
3
- extension :date_arithmetic
4
-
5
- # Used by the account verification and close account features
6
- create_table(:account_statuses) do
7
- Integer :id, :primary_key=>true
8
- String :name, :null=>false, :unique=>true
9
- end
10
- from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
11
-
12
- db = self
13
- create_table(:accounts) do
14
- primary_key :id, :type=>:Bignum
15
- foreign_key :status_id, :account_statuses, :null=>false, :default=>1
16
- if db.database_type == :postgres
17
- citext :email, :null=>false
18
- constraint :valid_email, :email=>/^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
19
- index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
20
- else
21
- String :email, :null=>false
22
- index :email, :unique=>true
23
- end
24
- end
25
-
26
- deadline_opts = proc do |days|
27
- if database_type == :mysql
28
- {:null=>false}
29
- else
30
- {:null=>false, :default=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :days=>days)}
31
- end
32
- end
33
-
34
- # Used by the password reset feature
35
- create_table(:account_password_reset_keys) do
36
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
37
- String :key, :null=>false
38
- DateTime :deadline, deadline_opts[1]
39
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
40
- end
41
-
42
- # Used by the refresh token feature
43
- create_table(:account_jwt_refresh_keys) do
44
- primary_key :id, :type=>:Bignum
45
- foreign_key :account_id, :accounts, :type=>:Bignum
46
- String :key, :null=>false
47
- DateTime :deadline, deadline_opts[1]
48
- end
49
-
50
- # Used by the account verification feature
51
- create_table(:account_verification_keys) do
52
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
53
- String :key, :null=>false
54
- DateTime :requested_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
55
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
56
- end
57
-
58
- # Used by the verify login change feature
59
- create_table(:account_login_change_keys) do
60
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
61
- String :key, :null=>false
62
- String :login, :null=>false
63
- DateTime :deadline, deadline_opts[1]
64
- end
65
-
66
- # Used by the remember me feature
67
- create_table(:account_remember_keys) do
68
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
69
- String :key, :null=>false
70
- DateTime :deadline, deadline_opts[14]
71
- end
72
-
73
- # Used by the lockout feature
74
- create_table(:account_login_failures) do
75
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
76
- Integer :number, :null=>false, :default=>1
77
- end
78
- create_table(:account_lockouts) do
79
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
80
- String :key, :null=>false
81
- DateTime :deadline, deadline_opts[1]
82
- DateTime :email_last_sent
83
- end
84
-
85
- # Used by the email auth feature
86
- create_table(:account_email_auth_keys) do
87
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
88
- String :key, :null=>false
89
- DateTime :deadline, deadline_opts[1]
90
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
91
- end
92
-
93
- # Used by the password expiration feature
94
- create_table(:account_password_change_times) do
95
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
96
- DateTime :changed_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
97
- end
98
-
99
- # Used by the account expiration feature
100
- create_table(:account_activity_times) do
101
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
102
- DateTime :last_activity_at, :null=>false
103
- DateTime :last_login_at, :null=>false
104
- DateTime :expired_at
105
- end
106
-
107
- # Used by the single session feature
108
- create_table(:account_session_keys) do
109
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
110
- String :key, :null=>false
111
- end
112
-
113
- # Used by the otp feature
114
- create_table(:account_otp_keys) do
115
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
116
- String :key, :null=>false
117
- Integer :num_failures, :null=>false, :default=>0
118
- Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
119
- end
120
-
121
- # Used by the recovery codes feature
122
- create_table(:account_recovery_codes) do
123
- foreign_key :id, :accounts, :type=>:Bignum
124
- String :code
125
- primary_key [:id, :code]
126
- end
127
-
128
- # Used by the sms codes feature
129
- create_table(:account_sms_codes) do
130
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
131
- String :phone_number, :null=>false
132
- Integer :num_failures
133
- String :code
134
- DateTime :code_issued_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
135
- end
136
-
137
- case database_type
138
- when :postgres
139
- user = get(Sequel.lit('current_user')) + '_password'
140
- run "GRANT REFERENCES ON accounts TO #{user}"
141
- when :mysql, :mssql
142
- user = if database_type == :mysql
143
- get(Sequel.lit('current_user')).sub(/_password@/, '@')
144
- else
145
- get(Sequel.function(:DB_NAME))
146
- end
147
- run "GRANT ALL ON account_statuses TO #{user}"
148
- run "GRANT ALL ON accounts TO #{user}"
149
- run "GRANT ALL ON account_password_reset_keys TO #{user}"
150
- run "GRANT ALL ON account_jwt_refresh_keys TO #{user}"
151
- run "GRANT ALL ON account_verification_keys TO #{user}"
152
- run "GRANT ALL ON account_login_change_keys TO #{user}"
153
- run "GRANT ALL ON account_remember_keys TO #{user}"
154
- run "GRANT ALL ON account_login_failures TO #{user}"
155
- run "GRANT ALL ON account_email_auth_keys TO #{user}"
156
- run "GRANT ALL ON account_lockouts TO #{user}"
157
- run "GRANT ALL ON account_password_change_times TO #{user}"
158
- run "GRANT ALL ON account_activity_times TO #{user}"
159
- run "GRANT ALL ON account_session_keys TO #{user}"
160
- run "GRANT ALL ON account_otp_keys TO #{user}"
161
- run "GRANT ALL ON account_recovery_codes TO #{user}"
162
- run "GRANT ALL ON account_sms_codes TO #{user}"
163
- end
164
- end
165
-
166
- down do
167
- drop_table(:account_sms_codes,
168
- :account_recovery_codes,
169
- :account_otp_keys,
170
- :account_session_keys,
171
- :account_activity_times,
172
- :account_password_change_times,
173
- :account_email_auth_keys,
174
- :account_lockouts,
175
- :account_login_failures,
176
- :account_remember_keys,
177
- :account_login_change_keys,
178
- :account_verification_keys,
179
- :account_jwt_refresh_keys,
180
- :account_password_reset_keys,
181
- :accounts,
182
- :account_statuses)
183
- end
184
- end