rodauth 2.25.0 → 2.26.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 934d5c19d29c583ebc73a057ba96a1c321741a64c965b22f4a243a42f56eab81
4
- data.tar.gz: 6398ec3d3bc1ee36a2195909b545ba5df9f0282c5a5b9136babb3538f21cb98e
3
+ metadata.gz: 4a071d4186e38bc942b2eeff4eb684dc5600f9b56e182b77bbdb1db1ecb0b26e
4
+ data.tar.gz: 828ee9405e19f0f368101ce7fa00c5db997dfe32137a5da36f842ac1ac350d03
5
5
  SHA512:
6
- metadata.gz: 32bae60cf66d97326397f3429d74ac2e7093afae74c59191bba5013ad6c7ec46782ddfa54c0a28b9a68c38cbf52b7e5071cd9d4eb7e434e5632c70842658120c
7
- data.tar.gz: b157ffa28b3f539c7618d747ab8cee321de6fbfdac11c603176b325ce54d10294a9b8860715a27c08e7d6128928e78d51e8f776b0201fcadec99075354c32f51
6
+ metadata.gz: 8813675142dc50ec6f2383a76e5f10a9fdc8071ff3be1f328cd44d51e910f2908546b3ad7db1b93f0f54e24318ab1b29c18fc8db5b978357212b93a11624cab6
7
+ data.tar.gz: b502557f7fffd20431601ecf85dde35451823eac85e8bd70334f6fc619cf03ea725439dffc8e362fc28a530a12520e74b1b681f4640477a0a95304396cc04422
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ === 2.26.0 (2022-10-21)
2
+
3
+ * Raise a more informative error when using a feature requiring hmac_secret but not setting hmac_secret (janko) (#271)
4
+
5
+ * Limit parameter bytesize to 1024 by default, override with max_param_bytesize configuration method (jeremyevans)
6
+
7
+ * Skip displaying links for disabled routes (janko) (#269)
8
+
9
+ * Do not prefix flash keys with the session key prefix (jeremyevans) (#266)
10
+
11
+ * Set configuration_name correctly for internal request classes (janko) (#265)
12
+
13
+ * Add argon2_secret configuration method to the argon2 feature to specify the secret/pepper used for argon2 password hashes (janko) (#264)
14
+
15
+ * Use white background instead of transparent background for QR code in otp feature (jeremyevans) (#256)
16
+
1
17
  === 2.25.0 (2022-06-22)
2
18
 
3
19
  * Support disabling routes by passing nil/false to *_route methods (janko) (#245)
data/README.rdoc CHANGED
@@ -80,7 +80,7 @@ features in use. These are development dependencies instead of
80
80
  runtime dependencies in the gem as it is possible to run without them:
81
81
 
82
82
  tilt :: Used by all features unless in JSON API only mode.
83
- rack_csrf :: Used for CSRF support if the :csrf=>:rack_csrf plugin
83
+ rack_csrf :: Used for CSRF support if the <tt>csrf: :rack_csrf</tt> plugin
84
84
  option is given (the default is to use Roda's route_csrf
85
85
  plugin, as that allows for more secure request-specific
86
86
  tokens).
@@ -336,18 +336,18 @@ When running the migration for the +ph+ user you'll need to modify a couple
336
336
  things for the schema changes:
337
337
 
338
338
  create_table(:account_password_hashes) do
339
- foreign_key :id, Sequel[:${DATABASE_NAME}][:accounts], :primary_key=>true, :type=>:Bignum
340
- String :password_hash, :null=>false
339
+ foreign_key :id, Sequel[:${DATABASE_NAME}][:accounts], primary_key: true, type: :Bignum
340
+ String :password_hash, null: false
341
341
  end
342
- Rodauth.create_database_authentication_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_password_hashes])
342
+ Rodauth.create_database_authentication_functions(self, table_name: Sequel[:${DATABASE_NAME}_password][:account_password_hashes])
343
343
 
344
344
  # if using the disallow_password_reuse feature:
345
345
  create_table(:account_previous_password_hashes) do
346
- primary_key :id, :type=>:Bignum
347
- foreign_key :account_id, Sequel[:${DATABASE_NAME}][:accounts], :type=>:Bignum
348
- String :password_hash, :null=>false
346
+ primary_key :id, type: :Bignum
347
+ foreign_key :account_id, Sequel[:${DATABASE_NAME}][:accounts], type: :Bignum
348
+ String :password_hash, null: false
349
349
  end
350
- Rodauth.create_database_previous_password_check_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_previous_password_hashes])
350
+ Rodauth.create_database_previous_password_check_functions(self, table_name: Sequel[:${DATABASE_NAME}_password][:account_previous_password_hashes])
351
351
 
352
352
  You'll also need to use the following Rodauth configuration methods so that the
353
353
  app account calls functions in a separate schema:
@@ -412,33 +412,33 @@ Note that these migrations require Sequel 4.35.0+.
412
412
 
413
413
  # Used by the account verification and close account features
414
414
  create_table(:account_statuses) do
415
- Integer :id, :primary_key=>true
416
- String :name, :null=>false, :unique=>true
415
+ Integer :id, primary_key: true
416
+ String :name, null: false, unique: true
417
417
  end
418
418
  from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
419
419
 
420
420
  db = self
421
421
  create_table(:accounts) do
422
- primary_key :id, :type=>:Bignum
423
- foreign_key :status_id, :account_statuses, :null=>false, :default=>1
422
+ primary_key :id, type: :Bignum
423
+ foreign_key :status_id, :account_statuses, null: false, default: 1
424
424
  if db.database_type == :postgres
425
- citext :email, :null=>false
426
- constraint :valid_email, :email=>/^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
425
+ citext :email, null: false
426
+ constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
427
427
  else
428
- String :email, :null=>false
428
+ String :email, null: false
429
429
  end
430
430
  if db.supports_partial_indexes?
431
- index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
431
+ index :email, unique: true, where: {status_id: [1, 2]}
432
432
  else
433
- index :email, :unique=>true
433
+ index :email, unique: true
434
434
  end
435
435
  end
436
436
 
437
437
  deadline_opts = proc do |days|
438
438
  if database_type == :mysql
439
- {:null=>false}
439
+ {null: false}
440
440
  else
441
- {:null=>false, :default=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :days=>days)}
441
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
442
442
  end
443
443
  end
444
444
 
@@ -452,140 +452,140 @@ Note that these migrations require Sequel 4.35.0+.
452
452
  String
453
453
  end
454
454
  create_table(:account_authentication_audit_logs) do
455
- primary_key :id, :type=>:Bignum
456
- foreign_key :account_id, :accounts, :null=>false, :type=>:Bignum
457
- DateTime :at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
458
- String :message, :null=>false
455
+ primary_key :id, type: :Bignum
456
+ foreign_key :account_id, :accounts, null: false, type: :Bignum
457
+ DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP
458
+ String :message, null: false
459
459
  column :metadata, json_type
460
- index [:account_id, :at], :name=>:audit_account_at_idx
461
- index :at, :name=>:audit_at_idx
460
+ index [:account_id, :at], name: :audit_account_at_idx
461
+ index :at, name: :audit_at_idx
462
462
  end
463
463
 
464
464
  # Used by the password reset feature
465
465
  create_table(:account_password_reset_keys) do
466
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
467
- String :key, :null=>false
466
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
467
+ String :key, null: false
468
468
  DateTime :deadline, deadline_opts[1]
469
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
469
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
470
470
  end
471
471
 
472
472
  # Used by the jwt refresh feature
473
473
  create_table(:account_jwt_refresh_keys) do
474
- primary_key :id, :type=>:Bignum
475
- foreign_key :account_id, :accounts, :null=>false, :type=>:Bignum
476
- String :key, :null=>false
474
+ primary_key :id, type: :Bignum
475
+ foreign_key :account_id, :accounts, null: false, type: :Bignum
476
+ String :key, null: false
477
477
  DateTime :deadline, deadline_opts[1]
478
- index :account_id, :name=>:account_jwt_rk_account_id_idx
478
+ index :account_id, name: :account_jwt_rk_account_id_idx
479
479
  end
480
480
 
481
481
  # Used by the account verification feature
482
482
  create_table(:account_verification_keys) do
483
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
484
- String :key, :null=>false
485
- DateTime :requested_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
486
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
483
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
484
+ String :key, null: false
485
+ DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP
486
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
487
487
  end
488
488
 
489
489
  # Used by the verify login change feature
490
490
  create_table(:account_login_change_keys) do
491
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
492
- String :key, :null=>false
493
- String :login, :null=>false
491
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
492
+ String :key, null: false
493
+ String :login, null: false
494
494
  DateTime :deadline, deadline_opts[1]
495
495
  end
496
496
 
497
497
  # Used by the remember me feature
498
498
  create_table(:account_remember_keys) do
499
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
500
- String :key, :null=>false
499
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
500
+ String :key, null: false
501
501
  DateTime :deadline, deadline_opts[14]
502
502
  end
503
503
 
504
504
  # Used by the lockout feature
505
505
  create_table(:account_login_failures) do
506
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
507
- Integer :number, :null=>false, :default=>1
506
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
507
+ Integer :number, null: false, default: 1
508
508
  end
509
509
  create_table(:account_lockouts) do
510
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
511
- String :key, :null=>false
510
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
511
+ String :key, null: false
512
512
  DateTime :deadline, deadline_opts[1]
513
513
  DateTime :email_last_sent
514
514
  end
515
515
 
516
516
  # Used by the email auth feature
517
517
  create_table(:account_email_auth_keys) do
518
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
519
- String :key, :null=>false
518
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
519
+ String :key, null: false
520
520
  DateTime :deadline, deadline_opts[1]
521
- DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
521
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
522
522
  end
523
523
 
524
524
  # Used by the password expiration feature
525
525
  create_table(:account_password_change_times) do
526
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
527
- DateTime :changed_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
526
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
527
+ DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP
528
528
  end
529
529
 
530
530
  # Used by the account expiration feature
531
531
  create_table(:account_activity_times) do
532
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
533
- DateTime :last_activity_at, :null=>false
534
- DateTime :last_login_at, :null=>false
532
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
533
+ DateTime :last_activity_at, null: false
534
+ DateTime :last_login_at, null: false
535
535
  DateTime :expired_at
536
536
  end
537
537
 
538
538
  # Used by the single session feature
539
539
  create_table(:account_session_keys) do
540
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
541
- String :key, :null=>false
540
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
541
+ String :key, null: false
542
542
  end
543
543
 
544
544
  # Used by the active sessions feature
545
545
  create_table(:account_active_session_keys) do
546
- foreign_key :account_id, :accounts, :type=>:Bignum
546
+ foreign_key :account_id, :accounts, type: :Bignum
547
547
  String :session_id
548
- Time :created_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
549
- Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
548
+ Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
549
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
550
550
  primary_key [:account_id, :session_id]
551
551
  end
552
552
 
553
553
  # Used by the webauthn feature
554
554
  create_table(:account_webauthn_user_ids) do
555
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
556
- String :webauthn_id, :null=>false
555
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
556
+ String :webauthn_id, null: false
557
557
  end
558
558
  create_table(:account_webauthn_keys) do
559
- foreign_key :account_id, :accounts, :type=>:Bignum
559
+ foreign_key :account_id, :accounts, type: :Bignum
560
560
  String :webauthn_id
561
- String :public_key, :null=>false
562
- Integer :sign_count, :null=>false
563
- Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
561
+ String :public_key, null: false
562
+ Integer :sign_count, null: false
563
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
564
564
  primary_key [:account_id, :webauthn_id]
565
565
  end
566
566
 
567
567
  # Used by the otp feature
568
568
  create_table(:account_otp_keys) do
569
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
570
- String :key, :null=>false
571
- Integer :num_failures, :null=>false, :default=>0
572
- Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
569
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
570
+ String :key, null: false
571
+ Integer :num_failures, null: false, default: 0
572
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
573
573
  end
574
574
 
575
575
  # Used by the recovery codes feature
576
576
  create_table(:account_recovery_codes) do
577
- foreign_key :id, :accounts, :type=>:Bignum
577
+ foreign_key :id, :accounts, type: :Bignum
578
578
  String :code
579
579
  primary_key [:id, :code]
580
580
  end
581
581
 
582
582
  # Used by the sms codes feature
583
583
  create_table(:account_sms_codes) do
584
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
585
- String :phone_number, :null=>false
584
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
585
+ String :phone_number, null: false
586
586
  Integer :num_failures
587
587
  String :code
588
- DateTime :code_issued_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
588
+ DateTime :code_issued_at, null: false, default: Sequel::CURRENT_TIMESTAMP
589
589
  end
590
590
 
591
591
  case database_type
@@ -652,8 +652,8 @@ Second migration, run using the +ph+ account:
652
652
  Sequel.migration do
653
653
  up do
654
654
  create_table(:account_password_hashes) do
655
- foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
656
- String :password_hash, :null=>false
655
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
656
+ String :password_hash, null: false
657
657
  end
658
658
  Rodauth.create_database_authentication_functions(self)
659
659
  case database_type
@@ -682,9 +682,9 @@ Second migration, run using the +ph+ account:
682
682
 
683
683
  # Used by the disallow_password_reuse feature
684
684
  create_table(:account_previous_password_hashes) do
685
- primary_key :id, :type=>:Bignum
686
- foreign_key :account_id, :accounts, :type=>:Bignum
687
- String :password_hash, :null=>false
685
+ primary_key :id, type: :Bignum
686
+ foreign_key :account_id, :accounts, type: :Bignum
687
+ String :password_hash, null: false
688
688
  end
689
689
  Rodauth.create_database_previous_password_check_functions(self)
690
690
 
@@ -725,8 +725,8 @@ To support multiple separate migration users, you can run the migration
725
725
  for the password user using Sequel's migration API:
726
726
 
727
727
  Sequel.extension :migration
728
- Sequel.postgres('DATABASE_NAME', :user=>'PASSWORD_USER_NAME') do |db|
729
- Sequel::Migrator.run(db, 'path/to/password_user/migrations', :table=>'schema_info_password')
728
+ Sequel.postgres('DATABASE_NAME', user: 'PASSWORD_USER_NAME') do |db|
729
+ Sequel::Migrator.run(db, 'path/to/password_user/migrations', table: 'schema_info_password')
730
730
  end
731
731
 
732
732
  If the database is not PostgreSQL, MySQL, or Microsoft SQL Server, or you
@@ -1112,7 +1112,7 @@ providing a name for any alternate configuration:
1112
1112
 
1113
1113
  plugin :rodauth do
1114
1114
  end
1115
- plugin :rodauth, :name=>:secondary do
1115
+ plugin :rodauth, name: :secondary do
1116
1116
  end
1117
1117
 
1118
1118
  Then in your routing code, any time you call rodauth, you can provide
@@ -1133,7 +1133,7 @@ alternate configurations. If you are using the remember feature in both
1133
1133
  configurations, you may also want to set a different remember key in the
1134
1134
  alternate configuration:
1135
1135
 
1136
- plugin :rodauth, :name=>:secondary do
1136
+ plugin :rodauth, name: :secondary do
1137
1137
  session_key_prefix "secondary_"
1138
1138
  remember_cookie_key "_secondary_remember"
1139
1139
  end
@@ -1235,7 +1235,7 @@ Facebook OAuth access token.
1235
1235
 
1236
1236
  account_from_login do |access_token|
1237
1237
  fb = Koala::Facebook::API.new(access_token)
1238
- if me = fb.get_object('me', :fields=>[:email])
1238
+ if me = fb.get_object('me', fields: [:email])
1239
1239
  me['email']
1240
1240
  end
1241
1241
  end
@@ -1369,7 +1369,7 @@ To add support for handling JSON responses, you can pass the +:json+
1369
1369
  option to the plugin, and enable the JWT feature in addition to
1370
1370
  other features you plan to use:
1371
1371
 
1372
- plugin :rodauth, :json=>true do
1372
+ plugin :rodauth, json: true do
1373
1373
  enable :login, :logout, :jwt
1374
1374
  end
1375
1375
 
@@ -1377,12 +1377,12 @@ If you do not want to load the HTML plugins that Rodauth usually loads
1377
1377
  (render, csrf, flash, h), because you are building a JSON-only API,
1378
1378
  pass <tt>:json => :only</tt>
1379
1379
 
1380
- plugin :rodauth, :json=>:only do
1380
+ plugin :rodauth, json: :only do
1381
1381
  enable :login, :logout, :jwt
1382
1382
  end
1383
1383
 
1384
1384
  Note that by default, the features that send email depend on the
1385
- render plugin, so if using the <tt>:json=>:only</tt> option, you
1385
+ render plugin, so if using the <tt>json: :only</tt> option, you
1386
1386
  either need to load the render plugin manually or you need to
1387
1387
  use the necessary *_email_body configuration options to specify
1388
1388
  the body of the emails.
@@ -1391,7 +1391,7 @@ The JWT feature enables JSON API support for all of the other features
1391
1391
  that Rodauth ships with. If you would like JSON API access that still uses
1392
1392
  rack session for storing session data, enable the JSON feature instead:
1393
1393
 
1394
- plugin :rodauth, :json=>true do
1394
+ plugin :rodauth, json: true do
1395
1395
  enable :login, :logout, :json
1396
1396
  only_json? true # if you want to only handle JSON requests
1397
1397
  end
data/doc/argon2.rdoc CHANGED
@@ -46,4 +46,5 @@ memory.
46
46
 
47
47
  == Auth Value Methods
48
48
 
49
+ argon2_secret :: A secret key used as input at hashing time, folded into the value of the hash.
49
50
  use_argon2? :: Whether to use the argon2 password hash algorithm for new passwords (true by default). The only reason to set this to false is if you have existing passwords using argon2 that you want to support, but want to use bcrypt for new passwords.
data/doc/base.rdoc CHANGED
@@ -60,6 +60,7 @@ login_required_error_status :: The response status to return when a login is req
60
60
  login_uses_email? :: Whether the login field uses email, used to set the type of the login field as well as the autocomplete setting.
61
61
  mark_input_fields_with_autocomplete? :: Whether input fields should be marked with autocomplete attribute appropriate for the field, true by default.
62
62
  mark_input_fields_with_inputmode? :: Whether input fields should be marked with inputmode attribute appropriate for the field, true by default.
63
+ max_param_bytesize :: The maximum bytesize allowed for submitted parameters, 1024 by default. Use nil for no limit.
63
64
  modifications_require_password? :: Whether making changes to an account requires the user reinputing their password. True by default if the account has a password.
64
65
  no_matching_login_error_status :: The response status to use when the login is not in the database, 401 by default.
65
66
  no_matching_login_message :: The error message to display when the login used is not in the database.
@@ -101,6 +102,7 @@ logged_in? :: Whether the current session is logged in.
101
102
  login_required :: Action to take when a login is required to access the page and the user is not logged in.
102
103
  null_byte_parameter_value(key, value) :: The value to use for the parameter if the parameter includes an ASCII NUL byte ("\0"), nil by default to ignore the parameter.
103
104
  open_account? :: Whether the current account is an open account (not closed or unverified).
105
+ over_max_bytesize_param_value(key, value) :: The value to use for the parameter if the parameter is over the maximum allowed bytesize, nil by default to ignore the parameter.
104
106
  password_match?(password) :: Check whether the given password matches the stored password hash.
105
107
  random_key :: A randomly generated string, used for creating tokens.
106
108
  redirect(path) :: Redirect the request to the given path.
@@ -15,6 +15,14 @@ If your database already contains password hashes that were created without a
15
15
  password pepper, these will get automatically updated with a password pepper
16
16
  next time the user successfully enters their password.
17
17
 
18
+ If you're using bcrypt (default), you should set +password_maximum_bytes+ so
19
+ that password + pepper don't exceed 72 bytes. This is because bcrypt truncates
20
+ passwords longer than 72 bytes, enabling an attacker to crack the pepper if the
21
+ password bytesize is unlimited. If you're using argon2, you should probably set
22
+ +argon2_secret+ instead of using this feature.
23
+
24
+ == Pepper Rotation
25
+
18
26
  You can rotate the password pepper as well, just make sure to add the previous
19
27
  pepper to the +previous_password_peppers+ array. Password hashes using the old
20
28
  pepper will get automatically updated on the next successful password match.
@@ -0,0 +1,45 @@
1
+ = New Features
2
+
3
+ * An argon2_secret configuration method has been added to the argon2
4
+ feature, supporting argon2's built-in password peppering.
5
+
6
+ = Other Improvements
7
+
8
+ * Links are no longer automatically displayed for routes that are
9
+ disabled by calling the *_route method with nil.
10
+
11
+ * The QR code used by the otp feature now uses a white background
12
+ instead of a transparent background, fixing issues when the
13
+ underlying background is dark.
14
+
15
+ * Input parameter bytesize is now limited to 1024 bytes by default.
16
+ Parameters larger than that will be ignored, as if they weren't
17
+ submitted.
18
+
19
+ * The Rodauth::Auth class for internal request classes now uses the
20
+ same configuration name as the class it is based on.
21
+
22
+ * The session_key_prefix configuration method no longer also prefixes
23
+ the keys used in the flash hash.
24
+
25
+ * The *_path and *_url methods now return nil when the related *_route
26
+ method returns nil, indicating the route is disabled.
27
+
28
+ * A more explicit error message is raised when using a feature that
29
+ requires the hmac_secret being set and not setting hmac_secret.
30
+
31
+ = Backwards Compatibility
32
+
33
+ * If you are using session_key_prefix and flash messages, you will
34
+ probably need to adjust your code to remove the prefix from the
35
+ expected flash keys, or manually prefix the flash keys by using
36
+ the flash_error_key and flash_notice_key configuration methods.
37
+
38
+ * The limiting of input parameter bytesizes by default could potentially
39
+ break applications that use Rodauth's parameter parsing method to
40
+ handle parameters that Rodauth itself doesn't handle. You can use
41
+ the max_param_bytesize configuration method to set a larger bytesize,
42
+ or use a value of nil with the method for the previous behavior of
43
+ no limit. Additionally, to customize the behavior if a parameter
44
+ is over the allowed bytesize, you can use the
45
+ over_max_bytesize_param_value configuration method.
@@ -12,6 +12,7 @@ module Rodauth
12
12
  Feature.define(:argon2, :Argon2) do
13
13
  depends :login_password_requirements_base
14
14
 
15
+ auth_value_method :argon2_secret, nil
15
16
  auth_value_method :use_argon2?, true
16
17
 
17
18
  private
@@ -35,7 +36,14 @@ module Rodauth
35
36
 
36
37
  def password_hash(password)
37
38
  return super unless use_argon2?
38
- ::Argon2::Password.new(password_hash_cost).create(password)
39
+
40
+ if secret = argon2_secret
41
+ argon2_params = Hash[password_hash_cost]
42
+ argon2_params[:secret] = secret
43
+ else
44
+ argon2_params = password_hash_cost
45
+ end
46
+ ::Argon2::Password.new(argon2_params).create(password)
39
47
  end
40
48
 
41
49
  def password_hash_match?(hash, password)
@@ -48,6 +56,7 @@ module Rodauth
48
56
 
49
57
  argon2_params = Hash[extract_password_hash_cost(salt)]
50
58
  argon2_params[argon2_salt_option] = Base64.decode64(salt.split('$').last)
59
+ argon2_params[:secret] = argon2_secret
51
60
  ::Argon2::Password.new(argon2_params).create(password)
52
61
  end
53
62
 
@@ -75,7 +84,7 @@ module Rodauth
75
84
  end
76
85
 
77
86
  def argon2_password_hash_match?(hash, password)
78
- ::Argon2::Password.verify_password(password, hash)
87
+ ::Argon2::Password.verify_password(password, hash, argon2_secret)
79
88
  end
80
89
  end
81
90
  end
@@ -24,8 +24,8 @@ module Rodauth
24
24
  auth_value_method :check_csrf_block, nil
25
25
  auth_value_method :check_csrf_opts, {}.freeze
26
26
  auth_value_method :default_redirect, '/'
27
- session_key :flash_error_key, :error
28
- session_key :flash_notice_key, :notice
27
+ flash_key :flash_error_key, :error
28
+ flash_key :flash_notice_key, :notice
29
29
  auth_value_method :hmac_secret, nil
30
30
  translatable_method :input_field_label_suffix, ''
31
31
  auth_value_method :input_field_error_class, 'error is-invalid'
@@ -37,6 +37,7 @@ module Rodauth
37
37
  auth_value_method :login_column, :email
38
38
  auth_value_method :login_required_error_status, 401
39
39
  auth_value_method :lockout_error_status, 403
40
+ auth_value_method :max_param_bytesize, 1024
40
41
  auth_value_method :password_hash_id_column, :id
41
42
  auth_value_method :password_hash_column, :password_hash
42
43
  auth_value_method :password_hash_table, :account_password_hashes
@@ -96,6 +97,7 @@ module Rodauth
96
97
  :login_required,
97
98
  :null_byte_parameter_value,
98
99
  :open_account?,
100
+ :over_max_bytesize_param_value,
99
101
  :password_match?,
100
102
  :random_key,
101
103
  :redirect,
@@ -455,11 +457,17 @@ module Rodauth
455
457
  value = raw_param(key)
456
458
  unless value.nil?
457
459
  value = value.to_s
458
- value = null_byte_parameter_value(key, value) if value.include?("\0")
460
+ value = over_max_bytesize_param_value(key, value) if max_param_bytesize && value.bytesize > max_param_bytesize
461
+ value = null_byte_parameter_value(key, value) if value && value.include?("\0")
459
462
  end
460
463
  value
461
464
  end
462
465
 
466
+ # Return nil by default for values over maximum bytesize.
467
+ def over_max_bytesize_param_value(key, value)
468
+ nil
469
+ end
470
+
463
471
  # Return nil by default for values with null bytes
464
472
  def null_byte_parameter_value(key, value)
465
473
  nil
@@ -541,7 +549,11 @@ module Rodauth
541
549
  end
542
550
 
543
551
  def convert_session_key(key)
544
- key = "#{session_key_prefix}#{key}".to_sym if session_key_prefix
552
+ key = :"#{session_key_prefix}#{key}" if session_key_prefix
553
+ normalize_session_or_flash_key(key)
554
+ end
555
+
556
+ def normalize_session_or_flash_key(key)
545
557
  scope.opts[:sessions_convert_symbols] ? key.to_s : key
546
558
  end
547
559
 
@@ -654,7 +666,7 @@ module Rodauth
654
666
  end
655
667
 
656
668
  def _account_from_login(login)
657
- ds = db[accounts_table].where(login_column=>login)
669
+ ds = account_table_ds.where(login_column=>login)
658
670
  ds = ds.select(*account_select) if account_select
659
671
  ds = ds.where(account_status_column=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks?
660
672
  ds.first
@@ -667,6 +679,8 @@ module Rodauth
667
679
  end
668
680
 
669
681
  def compute_raw_hmac(data)
682
+ raise ArgumentError, "hmac_secret not set" unless hmac_secret
683
+
670
684
  OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, hmac_secret, data)
671
685
  end
672
686
 
@@ -692,11 +706,15 @@ module Rodauth
692
706
 
693
707
  def account_ds(id=account_id)
694
708
  raise ArgumentError, "invalid account id passed to account_ds" unless id
695
- ds = db[accounts_table].where(account_id_column=>id)
709
+ ds = account_table_ds.where(account_id_column=>id)
696
710
  ds = ds.select(*account_select) if account_select
697
711
  ds
698
712
  end
699
713
 
714
+ def account_table_ds
715
+ db[accounts_table]
716
+ end
717
+
700
718
  def password_hash_ds
701
719
  db[password_hash_table].where(password_hash_id_column=>account ? account_id : session_value)
702
720
  end
@@ -761,6 +779,12 @@ module Rodauth
761
779
  end
762
780
  end
763
781
 
782
+ def _filter_links(links)
783
+ links.select!{|_, link| link}
784
+ links.sort!
785
+ links
786
+ end
787
+
764
788
  def internal_request?
765
789
  false
766
790
  end
@@ -340,6 +340,7 @@ module Rodauth
340
340
 
341
341
  klass = self.class
342
342
  internal_class = Class.new(klass)
343
+ internal_class.instance_variable_set(:@configuration_name, klass.configuration_name)
343
344
 
344
345
  if blocks = klass.instance_variable_get(:@internal_request_configuration_blocks)
345
346
  configuration = internal_class.configuration
@@ -20,11 +20,12 @@ module Rodauth
20
20
  session_key :login_redirect_session_key, :login_redirect
21
21
 
22
22
  auth_cached_method :multi_phase_login_forms
23
- auth_cached_method :login_form_footer_links
24
23
  auth_cached_method :login_form_footer
25
24
 
26
25
  auth_value_methods :login_return_to_requested_location_path
27
26
 
27
+ auth_private_methods :login_form_footer_links
28
+
28
29
  internal_request_method
29
30
  internal_request_method :valid_login_and_password?
30
31
 
@@ -125,6 +126,10 @@ module Rodauth
125
126
  "<input type='hidden' name=\"#{login_param}\" value=\"#{scope.h param(login_param)}\" />"
126
127
  end
127
128
 
129
+ def login_form_footer_links
130
+ @login_form_footer_links ||= _filter_links(_login_form_footer_links)
131
+ end
132
+
128
133
  def render_multi_phase_login_forms
129
134
  multi_phase_login_forms.sort.map{|_, form, _| form}.join("\n")
130
135
  end
@@ -308,7 +308,7 @@ module Rodauth
308
308
  end
309
309
 
310
310
  def otp_qr_code
311
- svg = RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true)
311
+ svg = RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true, :fill=>"#fff")
312
312
  svg.sub(/\A<\?xml version="1\.0" standalone="yes"\?>/, '')
313
313
  end
314
314
 
@@ -43,10 +43,6 @@ module Rodauth
43
43
  translatable_method :two_factor_disable_link_text, "Remove All Multifactor Authentication Methods"
44
44
  auth_value_method :two_factor_auth_return_to_requested_location?, false
45
45
 
46
- auth_cached_method :two_factor_auth_links
47
- auth_cached_method :two_factor_setup_links
48
- auth_cached_method :two_factor_remove_links
49
-
50
46
  auth_value_methods :two_factor_modifications_require_password?
51
47
 
52
48
  auth_methods(
@@ -57,6 +53,12 @@ module Rodauth
57
53
  :two_factor_update_session
58
54
  )
59
55
 
56
+ auth_private_methods(
57
+ :two_factor_auth_links,
58
+ :two_factor_setup_links,
59
+ :two_factor_remove_links
60
+ )
61
+
60
62
  internal_request_method :two_factor_disable
61
63
 
62
64
  route(:two_factor_manage, 'multifactor-manage') do |r|
@@ -206,6 +208,18 @@ module Rodauth
206
208
  nil
207
209
  end
208
210
 
211
+ def two_factor_auth_links
212
+ @two_factor_auth_links ||= _filter_links(_two_factor_auth_links)
213
+ end
214
+
215
+ def two_factor_setup_links
216
+ @two_factor_setup_links ||= _filter_links(_two_factor_setup_links)
217
+ end
218
+
219
+ def two_factor_remove_links
220
+ @two_factor_remove_links ||= _filter_links(_two_factor_remove_links)
221
+ end
222
+
209
223
  private
210
224
 
211
225
  def _two_factor_auth_links
@@ -6,7 +6,7 @@ module Rodauth
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 25
9
+ MINOR = 26
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
data/lib/rodauth.rb CHANGED
@@ -126,8 +126,8 @@ module Rodauth
126
126
  route_meth = :"#{name}_route"
127
127
  auth_value_method route_meth, default
128
128
 
129
- define_method(:"#{name}_path"){|opts={}| route_path(send(route_meth), opts)}
130
- define_method(:"#{name}_url"){|opts={}| route_url(send(route_meth), opts)}
129
+ define_method(:"#{name}_path"){|opts={}| route_path(send(route_meth), opts) if send(route_meth)}
130
+ define_method(:"#{name}_url"){|opts={}| route_url(send(route_meth), opts) if send(route_meth)}
131
131
 
132
132
  handle_meth = :"handle_#{name}"
133
133
  internal_handle_meth = :"_#{handle_meth}"
@@ -269,6 +269,11 @@ module Rodauth
269
269
  auth_value_methods(meth)
270
270
  end
271
271
 
272
+ def flash_key(meth, value)
273
+ define_method(meth){normalize_session_or_flash_key(value)}
274
+ auth_value_methods(meth)
275
+ end
276
+
272
277
  def auth_value_method(meth, value)
273
278
  define_method(meth){value}
274
279
  auth_value_methods(meth)
@@ -1,6 +1,6 @@
1
1
  #{rodauth.login_form_footer_links_heading}
2
2
  <ul class="rodauth-links rodauth-login-footer-links">
3
- #{rodauth.login_form_footer_links.sort.map do |_, link, text|
3
+ #{rodauth.login_form_footer_links.map do |_, link, text|
4
4
  "<li><a href=\"#{h link}\">#{h text}</a></li>"
5
5
  end.join("\n")}
6
6
  </ul>
@@ -1,5 +1,5 @@
1
1
  <ul class="rodauth-links rodauth-two-factor-auth-links">
2
- #{rodauth.two_factor_auth_links.sort.map do |_, link, text|
2
+ #{rodauth.two_factor_auth_links.map do |_, link, text|
3
3
  "<li><a href=\"#{h link}\">#{h text}</a></li>"
4
4
  end.join}
5
5
  </ul>
@@ -1,7 +1,7 @@
1
1
  #{rodauth.two_factor_setup_heading unless rodauth.two_factor_setup_links.empty?}
2
2
 
3
3
  <ul class="rodauth-links rodauth-multifactor-setup-links">
4
- #{rodauth.two_factor_setup_links.sort.map do |_, link, text|
4
+ #{rodauth.two_factor_setup_links.map do |_, link, text|
5
5
  "<li><a href=\"#{h link}\">#{h text}</a></li>"
6
6
  end.join("\n")}
7
7
  </ul>
@@ -9,7 +9,7 @@ end.join("\n")}
9
9
  #{rodauth.two_factor_remove_heading unless rodauth.two_factor_remove_links.empty?}
10
10
 
11
11
  <ul class="rodauth-links rodauth-multifactor-remove-links">
12
- #{rodauth.two_factor_remove_links.sort.map do |_, link, text|
12
+ #{rodauth.two_factor_remove_links.map do |_, link, text|
13
13
  "<li><a href=\"#{h link}\">#{h text}</a></li>"
14
14
  end.join("\n")}
15
15
  #{"<li><a href=\"#{h rodauth.two_factor_disable_path}\">#{rodauth.two_factor_disable_link_text}</a></li>" if rodauth.two_factor_remove_links.length > 1}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.25.0
4
+ version: 2.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-22 00:00:00.000000000 Z
11
+ date: 2022-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -341,6 +341,7 @@ extra_rdoc_files:
341
341
  - doc/release_notes/2.23.0.txt
342
342
  - doc/release_notes/2.24.0.txt
343
343
  - doc/release_notes/2.25.0.txt
344
+ - doc/release_notes/2.26.0.txt
344
345
  - doc/release_notes/2.3.0.txt
345
346
  - doc/release_notes/2.4.0.txt
346
347
  - doc/release_notes/2.5.0.txt
@@ -453,6 +454,7 @@ files:
453
454
  - doc/release_notes/2.23.0.txt
454
455
  - doc/release_notes/2.24.0.txt
455
456
  - doc/release_notes/2.25.0.txt
457
+ - doc/release_notes/2.26.0.txt
456
458
  - doc/release_notes/2.3.0.txt
457
459
  - doc/release_notes/2.4.0.txt
458
460
  - doc/release_notes/2.5.0.txt
@@ -588,7 +590,7 @@ metadata:
588
590
  documentation_uri: https://rodauth.jeremyevans.net/documentation.html
589
591
  mailing_list_uri: https://github.com/jeremyevans/rodauth/discussions
590
592
  source_code_uri: https://github.com/jeremyevans/rodauth
591
- post_install_message:
593
+ post_install_message:
592
594
  rdoc_options:
593
595
  - "--quiet"
594
596
  - "--line-numbers"
@@ -611,7 +613,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
611
613
  version: '0'
612
614
  requirements: []
613
615
  rubygems_version: 3.3.7
614
- signing_key:
616
+ signing_key:
615
617
  specification_version: 4
616
618
  summary: Authentication and Account Management Framework for Rack Applications
617
619
  test_files: []