rodauth 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +146 -0
- data/README.rdoc +644 -220
- data/Rakefile +99 -11
- data/doc/account_expiration.rdoc +55 -0
- data/doc/base.rdoc +104 -0
- data/doc/change_login.rdoc +29 -0
- data/doc/change_password.rdoc +26 -0
- data/doc/close_account.rdoc +31 -0
- data/doc/confirm_password.rdoc +22 -0
- data/doc/create_account.rdoc +34 -0
- data/doc/disallow_password_reuse.rdoc +37 -0
- data/doc/email_base.rdoc +19 -0
- data/doc/jwt.rdoc +35 -0
- data/doc/lockout.rdoc +83 -0
- data/doc/login.rdoc +27 -0
- data/doc/login_password_requirements_base.rdoc +50 -0
- data/doc/logout.rdoc +21 -0
- data/doc/otp.rdoc +100 -0
- data/doc/password_complexity.rdoc +50 -0
- data/doc/password_expiration.rdoc +52 -0
- data/doc/password_grace_period.rdoc +10 -0
- data/doc/recovery_codes.rdoc +60 -0
- data/doc/release_notes/1.0.0.txt +443 -0
- data/doc/remember.rdoc +82 -0
- data/doc/reset_password.rdoc +70 -0
- data/doc/session_expiration.rdoc +27 -0
- data/doc/single_session.rdoc +43 -0
- data/doc/sms_codes.rdoc +119 -0
- data/doc/two_factor_base.rdoc +27 -0
- data/doc/verify_account.rdoc +70 -0
- data/doc/verify_account_grace_period.rdoc +15 -0
- data/doc/verify_change_login.rdoc +9 -0
- data/lib/roda/plugins/rodauth.rb +3 -262
- data/lib/rodauth.rb +260 -0
- data/lib/rodauth/features/account_expiration.rb +108 -0
- data/lib/rodauth/features/base.rb +479 -0
- data/lib/rodauth/features/change_login.rb +77 -0
- data/lib/rodauth/features/change_password.rb +66 -0
- data/lib/rodauth/features/close_account.rb +82 -0
- data/lib/rodauth/features/confirm_password.rb +51 -0
- data/lib/rodauth/features/create_account.rb +128 -0
- data/lib/rodauth/features/disallow_password_reuse.rb +82 -0
- data/lib/rodauth/features/email_base.rb +63 -0
- data/lib/rodauth/features/jwt.rb +151 -0
- data/lib/rodauth/features/lockout.rb +262 -0
- data/lib/rodauth/features/login.rb +61 -0
- data/lib/rodauth/features/login_password_requirements_base.rb +123 -0
- data/lib/rodauth/features/logout.rb +37 -0
- data/lib/rodauth/features/otp.rb +338 -0
- data/lib/rodauth/features/password_complexity.rb +89 -0
- data/lib/rodauth/features/password_expiration.rb +111 -0
- data/lib/rodauth/features/password_grace_period.rb +46 -0
- data/lib/rodauth/features/recovery_codes.rb +240 -0
- data/lib/rodauth/features/remember.rb +200 -0
- data/lib/rodauth/features/reset_password.rb +207 -0
- data/lib/rodauth/features/session_expiration.rb +55 -0
- data/lib/rodauth/features/single_session.rb +87 -0
- data/lib/rodauth/features/sms_codes.rb +498 -0
- data/lib/rodauth/features/two_factor_base.rb +135 -0
- data/lib/rodauth/features/verify_account.rb +232 -0
- data/lib/rodauth/features/verify_account_grace_period.rb +76 -0
- data/lib/rodauth/features/verify_change_login.rb +20 -0
- data/lib/rodauth/migrations.rb +130 -0
- data/lib/rodauth/version.rb +9 -0
- data/spec/account_expiration_spec.rb +90 -0
- data/spec/all.rb +1 -0
- data/spec/change_login_spec.rb +149 -0
- data/spec/change_password_spec.rb +177 -0
- data/spec/close_account_spec.rb +162 -0
- data/spec/confirm_password_spec.rb +70 -0
- data/spec/create_account_spec.rb +127 -0
- data/spec/disallow_password_reuse_spec.rb +84 -0
- data/spec/lockout_spec.rb +228 -0
- data/spec/login_spec.rb +188 -0
- data/spec/migrate/001_tables.rb +103 -16
- data/spec/migrate/002_account_password_hash_column.rb +11 -0
- data/spec/migrate_password/001_tables.rb +60 -42
- data/spec/migrate_travis/001_tables.rb +116 -0
- data/spec/password_complexity_spec.rb +108 -0
- data/spec/password_expiration_spec.rb +243 -0
- data/spec/password_grace_period_spec.rb +93 -0
- data/spec/remember_spec.rb +424 -0
- data/spec/reset_password_spec.rb +185 -0
- data/spec/rodauth_spec.rb +57 -980
- data/spec/session_expiration_spec.rb +58 -0
- data/spec/single_session_spec.rb +107 -0
- data/spec/spec_helper.rb +202 -0
- data/spec/two_factor_spec.rb +1310 -0
- data/spec/verify_account_grace_period_spec.rb +135 -0
- data/spec/verify_account_spec.rb +142 -0
- data/spec/verify_change_login_spec.rb +46 -0
- data/spec/views/login.str +2 -2
- data/templates/add-recovery-codes.str +2 -0
- data/templates/button.str +5 -0
- data/templates/change-login.str +5 -18
- data/templates/change-password.str +6 -14
- data/templates/close-account.str +3 -6
- data/templates/confirm-password.str +4 -14
- data/templates/create-account.str +6 -30
- data/templates/login-confirm-field.str +6 -0
- data/templates/login-field.str +6 -0
- data/templates/login.str +5 -19
- data/templates/logout.str +2 -6
- data/templates/otp-auth-code-field.str +6 -0
- data/templates/otp-auth.str +8 -0
- data/templates/otp-disable.str +6 -0
- data/templates/otp-setup.str +21 -0
- data/templates/password-confirm-field.str +6 -0
- data/templates/password-field.str +6 -0
- data/templates/recovery-auth.str +12 -0
- data/templates/recovery-codes.str +6 -0
- data/templates/remember.str +8 -12
- data/templates/reset-password-request.str +2 -2
- data/templates/reset-password.str +4 -18
- data/templates/sms-auth.str +6 -0
- data/templates/sms-code-field.str +6 -0
- data/templates/sms-confirm.str +7 -0
- data/templates/sms-disable.str +7 -0
- data/templates/sms-request.str +5 -0
- data/templates/sms-setup.str +12 -0
- data/templates/unlock-account-request.str +3 -7
- data/templates/unlock-account.str +4 -7
- data/templates/verify-account-resend.str +2 -2
- data/templates/verify-account.str +2 -6
- metadata +191 -29
- data/lib/roda/plugins/rodauth/base.rb +0 -428
- data/lib/roda/plugins/rodauth/change_login.rb +0 -48
- data/lib/roda/plugins/rodauth/change_password.rb +0 -42
- data/lib/roda/plugins/rodauth/close_account.rb +0 -42
- data/lib/roda/plugins/rodauth/create_account.rb +0 -92
- data/lib/roda/plugins/rodauth/lockout.rb +0 -292
- data/lib/roda/plugins/rodauth/login.rb +0 -81
- data/lib/roda/plugins/rodauth/logout.rb +0 -36
- data/lib/roda/plugins/rodauth/remember.rb +0 -226
- data/lib/roda/plugins/rodauth/reset_password.rb +0 -205
- 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
|
-
|