rodauth 2.15.0 → 2.19.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: 3abad4574361365b90229229928b653d049ad73e2e366195e10bb8ee565de323
4
- data.tar.gz: 2fecb89ba5456f9aa8569eef019dbf7f9b7a3b7736b3da053ce15e5b4d67074c
3
+ metadata.gz: 0defa94cb0c58b317997853eda1775694a8b0bb89e3bb75f8de50af57a2223fe
4
+ data.tar.gz: 977738446cb7d8ac53a7edeab2587c07e7a9b9407d2584daa80fe6d47147f395
5
5
  SHA512:
6
- metadata.gz: 9086a3f18f04da0184af7fcaaa3c474ae3560e7b9c1b6b526cbbd8239ca4f2f47f2a44fe4e8681e349c5bb78a6e3f7529b5bd296d62ad92e1a1ac9188122a4ab
7
- data.tar.gz: 2b67d509e6a0b0a61ca679f45238714b817d6eef1becb9e1ecfeab33d7e296d3f43ffa0a392d812981b8a3f096cbc868501c00f6661d1c72194cdfe0726ef842
6
+ metadata.gz: 0e8833fc2ac01f3a917b8c267642b92472969531df33dc90dcae6e90d5b62befbf2f948ca75cafcd84353b4b9d629f1dec8d70e3e77cc2387cc0791cee551773
7
+ data.tar.gz: 03ff22811e9679b26f1dc83c676ca19145c762d26c216a9aafae816d3bad67cd39a68430fbd72c55f17e77117fbcd65690eb6d71f3b05f72860833a2205f2fe7
data/CHANGELOG CHANGED
@@ -1,3 +1,41 @@
1
+ === 2.19.0 (2021-12-22)
2
+
3
+ * Add login_maximum_bytes, setting the maximum number of bytes in a login, 255 by default (jeremyevans)
4
+
5
+ * Add password_maximum_bytes, setting the maximum number of bytes in a password, nil by default for no limit (jeremyevans)
6
+
7
+ * Add password_maximum_length, setting the maximum number of characters in a password, nil by default for no limit (jeremyevans)
8
+
9
+ * Support multi-level inheritance of Rodauth::Auth (janko) (#191)
10
+
11
+ * Allow internal_request feature to work correctly when loaded into custom Rodauth::Auth subclasses before loading into a Roda application (janko) (#190)
12
+
13
+ * Assign internal subclass created by internal_request feature to the InternalRequest constant (janko) (#187)
14
+
15
+ === 2.18.0 (2021-11-23)
16
+
17
+ * Allow JSON API access to /multifactor-manage to get links to setup/disable multifactor authentication endpoints (jeremyevans)
18
+
19
+ * Allow JSON API access to /multifactor-auth to get links to possible multifactor authentication endpoints (jeremyevans)
20
+
21
+ * Set configuration_name on class passed via :auth_class option if not already set (janko, jeremyevans) (#181)
22
+
23
+ * Use viewbox: true option when creating QR code in otp feature, displays better and easier to style when using rqrcode 2+ (jeremyevans)
24
+
25
+ * Make argon2 feature work with argon2 2.1.0 (jeremyevans)
26
+
27
+ === 2.17.0 (2021-09-24)
28
+
29
+ * Make jwt_refresh work correctly with verify_account_grace_period (jeremyevans)
30
+
31
+ * Use 4xx status code when attempting to login to or create an unverified account (janko) (#177, #178)
32
+
33
+ === 2.16.0 (2021-08-23)
34
+
35
+ * Add Rodauth.lib for using Rodauth as a library (jeremyevans)
36
+
37
+ * Make internal_request feature work if the configuration uses only_json? true (janko) (#176)
38
+
1
39
  === 2.15.0 (2021-07-27)
2
40
 
3
41
  * Add path_class_methods feature, for getting paths/URLs using class methods (jeremyevans)
data/README.rdoc CHANGED
@@ -69,7 +69,8 @@ Website :: http://rodauth.jeremyevans.net
69
69
  Demo Site :: http://rodauth-demo.jeremyevans.net
70
70
  Source :: http://github.com/jeremyevans/rodauth
71
71
  Bugs :: http://github.com/jeremyevans/rodauth/issues
72
- Google Group :: https://groups.google.com/forum/#!forum/rodauth
72
+ Discussion Forum (GitHub Discussions) :: https://github.com/jeremyevans/rodauth/discussions
73
+ Alternate Discussion Forum (Google Groups) :: https://groups.google.com/forum/#!forum/rodauth
73
74
 
74
75
  == Dependencies
75
76
 
@@ -422,9 +423,12 @@ Note that these migrations require Sequel 4.35.0+.
422
423
  if db.database_type == :postgres
423
424
  citext :email, :null=>false
424
425
  constraint :valid_email, :email=>/^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
425
- index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
426
426
  else
427
427
  String :email, :null=>false
428
+ end
429
+ if db.supports_partial_indexes?
430
+ index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
431
+ else
428
432
  index :email, :unique=>true
429
433
  end
430
434
  end
@@ -1073,6 +1077,27 @@ methods on that class to perform actions on behalf of a user. See the
1073
1077
  {internal request feature documentation}[rdoc-ref:doc/internal_request.rdoc]
1074
1078
  for details.
1075
1079
 
1080
+ == Using Rodauth as a Library
1081
+
1082
+ Rodauth was designed to serve as an authentication framework for Rack applications.
1083
+ However, Rodauth can be used purely as a library outside of a web application. You
1084
+ can do this by requiring +rodauth+, and using the +Rodauth.lib+ method to return
1085
+ a <tt>Rodauth::Auth</tt> subclass, which you can call methods on. You pass the
1086
+ +Rodauth.lib+ method an optional hash of Rodauth plugin options and a Rodauth
1087
+ configuration block:
1088
+
1089
+ require 'rodauth'
1090
+ rodauth = Rodauth.lib do
1091
+ enable :create_account, :change_password
1092
+ end
1093
+ rodauth.create_account(login: 'foo@example.com', password: '...')
1094
+ rodauth.change_password(account_id: 24601, password: '...')
1095
+
1096
+ This supports builds on top of the internal_request support (it implicitly loads
1097
+ the internal_request feature before processing the configuration block), and
1098
+ allows the use of Rodauth in non-web applications. Note that you still have to
1099
+ setup a Sequel::Database connection for Rodauth to use for data storage.
1100
+
1076
1101
  === With Multiple Configurations
1077
1102
 
1078
1103
  Rodauth supports using multiple rodauth configurations in the same
@@ -44,6 +44,7 @@ Rodauth will call +set_error_reason+ with:
44
44
  * :login_not_valid_email
45
45
  * :login_required
46
46
  * :login_too_long
47
+ * :login_too_many_bytes
47
48
  * :login_too_short
48
49
  * :logins_do_not_match
49
50
  * :no_current_sms_code
@@ -56,6 +57,8 @@ Rodauth will call +set_error_reason+ with:
56
57
  * :password_in_dictionary
57
58
  * :password_is_one_of_the_most_common
58
59
  * :password_same_as_previous_password
60
+ * :password_too_long
61
+ * :password_too_many_bytes
59
62
  * :password_too_short
60
63
  * :passwords_do_not_match
61
64
  * :same_as_current_login
@@ -0,0 +1,19 @@
1
+ = Change table and column names
2
+
3
+ All tables that Rodauth uses will have a configuration method that ends with
4
+ +_table+ for configuring the table name. For example, if you store user accounts
5
+ in the +users+ table instead of +accounts+ table, you can use the following
6
+ in your configuration:
7
+
8
+ accounts_table :users
9
+
10
+ All columns that Rodauth uses will have a configuration method that ends with
11
+ +_column+ for configuring the column name. For example, if you are storing the
12
+ login for accounts in the +login+ column instead of the +email+ column, you
13
+ can use the following in your configuration:
14
+
15
+ login_column :login
16
+
17
+ Please see the documentation for Rodauth features for the names of the
18
+ configuration methods that you can use. You can see the default values for
19
+ the tables and columns in the {"Creating tables" section of the README}[rdoc-ref:README.rdoc].
data/doc/guides/i18n.rdoc CHANGED
@@ -24,3 +24,6 @@ Your translation file may then look something like this:
24
24
  require_login_error_flash: "Login is required for accessing this page"
25
25
  no_matching_login_message: "user with this email address doesn't exist"
26
26
  reset_password_email_subject: "Password Reset Instructions"
27
+
28
+ Alternatively, you can use the
29
+ {rodauth-i18n}[https://github.com/janko/rodauth-i18n] gem.
@@ -0,0 +1,34 @@
1
+ = Share configuration via inheritance
2
+
3
+ If you have multiple configurations that needs to share some amount of
4
+ authentication behaviour, you can do so through inheritance. For example:
5
+
6
+ require "rodauth"
7
+
8
+ class RodauthBase < Rodauth::Auth
9
+ configure do
10
+ # common authentication configuration
11
+ end
12
+ end
13
+
14
+ class RodauthMain < RodauthBase # inherit common configuration
15
+ configure do
16
+ # main-specific authentication configuration
17
+ end
18
+ end
19
+
20
+ class RodauthAdmin < RodauthBase # inherit common configuration
21
+ configure do
22
+ # admin-specific authentication configuration
23
+ end
24
+ end
25
+
26
+ class RodauthApp < Roda
27
+ plugin :rodauth, auth_class: RodauthMain
28
+ plugin :rodauth, auth_class: RodauthAdmin, name: :admin
29
+ # ...
30
+ end
31
+
32
+ However, when doing this, you need to be careful that you do not use a
33
+ configuration method in a superclass, and then load a feature in a subclass
34
+ that overrides the configuration you set in the superclass.
@@ -11,17 +11,23 @@ login_confirm_label :: The label to use for login confirmations.
11
11
  login_confirm_param :: The parameter name to use for login confirmations.
12
12
  login_does_not_meet_requirements_message :: The error message to display when the login does not meet the requirements you have set.
13
13
  login_email_regexp :: The regular expression used to validate whether login is a valid email address.
14
- login_maximum_length :: The maximum length for logins, 255 by default.
15
- login_minimum_length :: The minimum length for logins, 3 by default.
14
+ login_maximum_bytes :: The maximum length for logins in bytes, 255 by default.
15
+ login_maximum_length :: The maximum length for logins in characters, 255 by default.
16
+ login_minimum_length :: The minimum length for logins in characters, 3 by default.
16
17
  login_not_valid_email_message :: The error message to display when login is not a valid email address.
17
18
  login_too_long_message :: The error message fragment to show if the login is too long.
19
+ login_too_many_bytes_message :: The error message fragment to show if the login has too many bytes.
18
20
  login_too_short_message :: The error message fragment to show if the login is too short.
19
21
  logins_do_not_match_message :: The error message to display when login and login confirmation do not match.
20
22
  password_confirm_label :: The label to use for password confirmations.
21
23
  password_confirm_param :: The parameter name to use for password confirmations.
22
24
  password_does_not_meet_requirements_message :: The error message to display when the password does not meet the requirements you have set.
23
25
  password_hash_cost :: The cost to use for the password hash algorithm. This should be an integer when using bcrypt (the default), and a hash if using argon2 (supported by the argon2 feature).
24
- password_minimum_length :: The minimum length for passwords, 6 by default.
26
+ password_maximum_bytes :: The maximum length for passwords in bytes, nil by default for no limit. bcrypt only uses the first 72 bytes of the password when creating the password hash, so if you are using bcrypt as the password hash function, you may want to set this to 72.
27
+ password_maximum_length :: The maximum length for passwords in characters, nil by default for no limit.
28
+ password_minimum_length :: The minimum length for passwords in characters, 6 by default.
29
+ password_too_long_message :: The error message fragment to show if the password is too long.
30
+ password_too_many_bytes_message :: The error message fragment to show if the password is has too many bytes.
25
31
  password_too_short_message :: The error message fragment to show if the password is too short.
26
32
  passwords_do_not_match_message :: The error message to display when password and password confirmation do not match.
27
33
  require_email_address_logins? :: Whether logins need to be valid email addresses, true by default.
@@ -0,0 +1,20 @@
1
+ = New Features
2
+
3
+ * Rodauth.lib has been added for using Rodauth purely as a library,
4
+ useful in non-web applications:
5
+
6
+ require 'rodauth'
7
+ rodauth = Rodauth.lib do
8
+ enable :create_account, :change_password
9
+ end
10
+ rodauth.create_account(login: 'foo@example.com', password: '...')
11
+ rodauth.change_password(account_id: 24601, password: '...')
12
+
13
+ This is built on top of the internal_request feature, and works by
14
+ creating a Roda application with the rodauth plugin, and returning
15
+ the related Rodauth::Auth class.
16
+
17
+ = Other Improvements
18
+
19
+ * The internal_request feature now works correctly for configurations
20
+ where only_json? is set to true.
@@ -0,0 +1,10 @@
1
+ = Improvements
2
+
3
+ * The jwt_refresh feature now works for unverified accounts when using
4
+ the verify_account_grace_period feature.
5
+
6
+ * When trying to create an account that already exists but is
7
+ unverified, Rodauth now returns a 4xx response.
8
+
9
+ * When trying to login to an unverified account, Rodauth now returns a
10
+ 4xx response.
@@ -0,0 +1,27 @@
1
+ = New Features
2
+
3
+ * When using the json and multifactor auth features, the JSON API can
4
+ now access the multifactor-manage route to get lists of endpoints
5
+ for setting up and disabling supported multifactor authentication
6
+ methods. The JSON API can now also access the multifactor-auth
7
+ route to get a list of endpoints for multifactor authentication for
8
+ the currently logged in account.
9
+
10
+ = Other Improvements
11
+
12
+ * In the otp feature, the viewbox: true rqrcode option is now used
13
+ when creating the QR code. This results in a QR code that is
14
+ displayed better and is easier to style. This option only has
15
+ an effect when using rqrcode 2+.
16
+
17
+ * When using the :auth_class option when loading the rodauth plugin,
18
+ the configuration name is set in the provided auth class, unless the
19
+ auth class already has a configuration name set.
20
+
21
+ * The example migration now recommends using a partial index on the
22
+ email column in cases where the database supports partial indexes.
23
+ Previously, it only recommended it on PostgreSQL.
24
+
25
+ * The argon2 feature now works with argon2 2.1.0. Older versions of
26
+ Rodauth work with both earlier and later versions of argon2, but
27
+ not 2.1.0.
@@ -0,0 +1,61 @@
1
+ = New Features
2
+
3
+ * A login_maximum_bytes configuration method has been added, setting
4
+ the maximum bytes allowed in a login. This was added as
5
+ login_maximum_length sets the maximum length in characters. It's
6
+ possible a different number of maximum bytes than maximum
7
+ characters is desired by some applications, and since the database
8
+ column size may be enforced in bytes, it's useful to have a check
9
+ before trying a database query that would raise an exception. This
10
+ default value for login_maximum_bytes is 255, the same as the
11
+ default value for login_maximum_length.
12
+
13
+ A login_too_many_bytes_message configuration method has been added
14
+ for customizing the error message if a login has too many bytes.
15
+
16
+ * password_maximum_length and password_maximum_bytes configuration
17
+ methods have been added, specifying the maximum size of passwords
18
+ in characters and bytes, respectively. Both configurations default
19
+ to nil, meaning no limit, so there is no change in default behavior.
20
+
21
+ The bcrypt algorithm only uses the first 72 bytes of a password, and
22
+ in some environments it may be desirable to reject passwords over
23
+ that limit. password_too_long_message and
24
+ password_too_many_bytes_message configuration methods have been
25
+ added for customizing the error messages used for passwords that are
26
+ too long.
27
+
28
+ Note that in most environments, if you want to support passwords
29
+ over 72 bytes and have the entire password be considered, you should
30
+ probably use the argon2 feature.
31
+
32
+ = Other Improvements
33
+
34
+ * The subclass created by the internal_request feature is now set
35
+ to the InternalRequest constant on the superclass, mostly to
36
+ make identifying it easier in inspect output.
37
+
38
+ * Support has been improved for custom Rodauth::Auth subclasses that
39
+ load features before the subclass is loaded into Roda, by delaying
40
+ the call to post_configure until the subclass is loaded into Roda.
41
+ Among other things, this fixes the use of the internal_request
42
+ feature in such classes.
43
+
44
+ * Multi-level inheritance of Rodauth::Auth is now supported. This can
45
+ be useful as a way to share custom authentication settings between
46
+ multiple Rodauth configurations. However, users of multi-level
47
+ inheritance should be careful not to load features in subclasses
48
+ that override custom settings in superclasses.
49
+
50
+ = Other
51
+
52
+ * Rodauth's primary discussion forum is now GitHub Discussions. The
53
+ rodauth Google Group is still available for users who would prefer
54
+ to use that instead.
55
+
56
+ = Backwards Compatibility
57
+
58
+ * The addition of login_maximum_bytes with a default value of 255 is
59
+ backwards incompatible for applications that want to support logins
60
+ with multibyte characters where the number of characters in the
61
+ login is at or below 255, but the number of bytes is above 255.
@@ -16,6 +16,18 @@ module Rodauth
16
16
 
17
17
  private
18
18
 
19
+ if Argon2::VERSION != '2.1.0'
20
+ def argon2_salt_option
21
+ :salt_do_not_supply
22
+ end
23
+ # :nocov:
24
+ else
25
+ def argon2_salt_option
26
+ :salt_for_testing_purposes_only
27
+ end
28
+ # :nocov:
29
+ end
30
+
19
31
  def password_hash_cost
20
32
  return super unless use_argon2?
21
33
  argon2_hash_cost
@@ -35,7 +47,7 @@ module Rodauth
35
47
  return super unless argon2_hash_algorithm?(salt)
36
48
 
37
49
  argon2_params = Hash[extract_password_hash_cost(salt)]
38
- argon2_params[:salt_do_not_supply] = Base64.decode64(salt.split('$').last)
50
+ argon2_params[argon2_salt_option] = Base64.decode64(salt.split('$').last)
39
51
  ::Argon2::Password.new(argon2_params).create(password)
40
52
  end
41
53
 
@@ -92,6 +92,10 @@ module Rodauth
92
92
  @internal_request_return_value
93
93
  end
94
94
 
95
+ def only_json?
96
+ false
97
+ end
98
+
95
99
  private
96
100
 
97
101
  def internal_request?
@@ -335,14 +339,7 @@ module Rodauth
335
339
  return if is_a?(InternalRequestMethods)
336
340
 
337
341
  klass = self.class
338
- internal_class = Class.new(klass) do
339
- @roda_class = klass.roda_class
340
- @features = klass.features.clone
341
- @routes = klass.routes.clone
342
- @route_hash = klass.route_hash.clone
343
- @configuration = klass.configuration.clone
344
- @configuration.instance_variable_set(:@auth, self)
345
- end
342
+ internal_class = Class.new(klass)
346
343
 
347
344
  if blocks = klass.instance_variable_get(:@internal_request_configuration_blocks)
348
345
  configuration = internal_class.configuration
@@ -362,6 +359,9 @@ module Rodauth
362
359
  end
363
360
  end
364
361
  end
362
+
363
+ klass.const_set(:InternalRequest, internal_class)
364
+ klass.private_constant :InternalRequest
365
365
  end
366
366
  end
367
367
  end
@@ -67,6 +67,25 @@ module Rodauth
67
67
 
68
68
  private
69
69
 
70
+ def before_two_factor_manage_route
71
+ super if defined?(super)
72
+ if use_json?
73
+ json_response[:setup_links] = two_factor_setup_links.sort.map{|_,link| link}
74
+ json_response[:remove_links] = two_factor_remove_links.sort.map{|_,link| link}
75
+ json_response[json_response_success_key] ||= "" if include_success_messages?
76
+ return_json_response
77
+ end
78
+ end
79
+
80
+ def before_two_factor_auth_route
81
+ super if defined?(super)
82
+ if use_json?
83
+ json_response[:auth_links] = two_factor_auth_links.sort.map{|_,link| link}
84
+ json_response[json_response_success_key] ||= "" if include_success_messages?
85
+ return_json_response
86
+ end
87
+ end
88
+
70
89
  def before_view_recovery_codes
71
90
  super if defined?(super)
72
91
  if use_json?
@@ -98,7 +98,7 @@ module Rodauth
98
98
  # JWT is invalid for other reasons. Make sure the expiration is the
99
99
  # only reason the JWT isn't valid before treating this as an expired token.
100
100
  JWT.decode(jwt_token, jwt_secret, true, Hash[jwt_decode_opts].merge!(:verify_expiration=>false, :algorithm=>jwt_algorithm))[0]
101
- rescue => e
101
+ rescue
102
102
  else
103
103
  json_response[json_response_error_key] = expired_jwt_access_token_message
104
104
  response.status ||= expired_jwt_access_token_status
@@ -120,7 +120,7 @@ module Rodauth
120
120
  end
121
121
 
122
122
  ds = account_ds(id)
123
- ds = ds.where(account_status_column=>account_open_status_value) unless skip_status_checks?
123
+ ds = ds.where(account_session_status_filter) unless skip_status_checks?
124
124
  ds.first
125
125
  end
126
126
 
@@ -7,10 +7,13 @@ module Rodauth
7
7
  auth_value_method :login_email_regexp, /\A[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+\z/
8
8
  auth_value_method :login_minimum_length, 3
9
9
  auth_value_method :login_maximum_length, 255
10
+ auth_value_method :login_maximum_bytes, 255
10
11
  translatable_method :login_not_valid_email_message, 'not a valid email address'
11
12
  translatable_method :logins_do_not_match_message, 'logins do not match'
12
13
  auth_value_method :password_confirm_param, 'password-confirm'
13
14
  auth_value_method :password_minimum_length, 6
15
+ auth_value_method :password_maximum_bytes, nil
16
+ auth_value_method :password_maximum_length, nil
14
17
  translatable_method :passwords_do_not_match_message, 'passwords do not match'
15
18
  auth_value_method :require_email_address_logins?, true
16
19
  auth_value_method :require_login_confirmation?, true
@@ -22,10 +25,13 @@ module Rodauth
22
25
  :login_confirm_label,
23
26
  :login_does_not_meet_requirements_message,
24
27
  :login_too_long_message,
28
+ :login_too_many_bytes_message,
25
29
  :login_too_short_message,
26
30
  :password_confirm_label,
27
31
  :password_does_not_meet_requirements_message,
28
32
  :password_hash_cost,
33
+ :password_too_long_message,
34
+ :password_too_many_bytes_message,
29
35
  :password_too_short_message
30
36
  )
31
37
 
@@ -78,6 +84,14 @@ module Rodauth
78
84
  "invalid password, does not meet requirements#{" (#{password_requirement_message})" if password_requirement_message}"
79
85
  end
80
86
 
87
+ def password_too_long_message
88
+ "maximum #{password_maximum_length} characters"
89
+ end
90
+
91
+ def password_too_many_bytes_message
92
+ "maximum #{password_maximum_bytes} bytes"
93
+ end
94
+
81
95
  def password_too_short_message
82
96
  "minimum #{password_minimum_length} characters"
83
97
  end
@@ -95,6 +109,10 @@ module Rodauth
95
109
  "maximum #{login_maximum_length} characters"
96
110
  end
97
111
 
112
+ def login_too_many_bytes_message
113
+ "maximum #{login_maximum_bytes} bytes"
114
+ end
115
+
98
116
  def login_too_short_message
99
117
  "minimum #{login_minimum_length} characters"
100
118
  end
@@ -111,6 +129,9 @@ module Rodauth
111
129
  elsif login_maximum_length < login.length
112
130
  set_login_requirement_error_message(:login_too_long, login_too_long_message)
113
131
  false
132
+ elsif login_maximum_bytes < login.bytesize
133
+ set_login_requirement_error_message(:login_too_many_bytes, login_too_many_bytes_message)
134
+ false
114
135
  else
115
136
  true
116
137
  end
@@ -128,9 +149,18 @@ module Rodauth
128
149
  end
129
150
 
130
151
  def password_meets_length_requirements?(password)
131
- return true if password_minimum_length <= password.length
132
- set_password_requirement_error_message(:password_too_short, password_too_short_message)
133
- false
152
+ if password_minimum_length > password.length
153
+ set_password_requirement_error_message(:password_too_short, password_too_short_message)
154
+ false
155
+ elsif password_maximum_length && password_maximum_length < password.length
156
+ set_password_requirement_error_message(:password_too_long, password_too_long_message)
157
+ false
158
+ elsif password_maximum_bytes && password_maximum_bytes < password.bytesize
159
+ set_password_requirement_error_message(:password_too_many_bytes, password_too_many_bytes_message)
160
+ false
161
+ else
162
+ true
163
+ end
134
164
  end
135
165
 
136
166
  def password_does_not_contain_null_byte?(password)
@@ -303,7 +303,7 @@ module Rodauth
303
303
  end
304
304
 
305
305
  def otp_qr_code
306
- RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8)
306
+ RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true)
307
307
  end
308
308
 
309
309
  def otp_user_key
@@ -196,8 +196,7 @@ module Rodauth
196
196
 
197
197
  def new_account(login)
198
198
  if account_from_login(login) && allow_resending_verify_account_email?
199
- set_redirect_error_status(unopen_account_error_status)
200
- set_error_reason :already_an_unverified_account_with_this_login
199
+ set_response_error_reason_status(:already_an_unverified_account_with_this_login, unopen_account_error_status)
201
200
  set_error_flash attempt_to_create_unverified_account_error_flash
202
201
  response.write resend_verify_account_view
203
202
  request.halt
@@ -274,8 +273,7 @@ module Rodauth
274
273
 
275
274
  def before_login_attempt
276
275
  unless open_account?
277
- set_redirect_error_status(unopen_account_error_status)
278
- set_error_reason :unverified_account
276
+ set_response_error_reason_status(:unverified_account, unopen_account_error_status)
279
277
  set_error_flash attempt_to_login_to_unverified_account_error_flash
280
278
  response.write resend_verify_account_view
281
279
  request.halt
@@ -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 = 15
9
+ MINOR = 19
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
@@ -3,6 +3,17 @@
3
3
  require 'securerandom'
4
4
 
5
5
  module Rodauth
6
+ def self.lib(opts={}, &block)
7
+ require 'roda'
8
+ c = Class.new(Roda)
9
+ c.plugin(:rodauth, opts) do
10
+ enable :internal_request
11
+ instance_exec(&block)
12
+ end
13
+ c.freeze
14
+ c.rodauth
15
+ end
16
+
6
17
  def self.load_dependencies(app, opts={})
7
18
  json_opt = opts.fetch(:json, app.opts[:rodauth_json])
8
19
  if json_opt
@@ -39,14 +50,16 @@ module Rodauth
39
50
  else
40
51
  json_opt != :only
41
52
  end
42
- auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth){@configuration_name = opts[:name]}
53
+ auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth)
43
54
  if !auth_class.roda_class
44
55
  auth_class.roda_class = app
45
56
  elsif auth_class.roda_class != app
46
- auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class){@configuration_name = opts[:name]}
57
+ auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class)
47
58
  auth_class.roda_class = app
48
59
  end
60
+ auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)}
49
61
  auth_class.configure(&block) if block
62
+ auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure)
50
63
  end
51
64
 
52
65
  FEATURES = {}
@@ -259,38 +272,6 @@ module Rodauth
259
272
  end
260
273
  end
261
274
 
262
- class Auth
263
- class << self
264
- attr_accessor :roda_class
265
- attr_reader :features
266
- attr_reader :routes
267
- attr_accessor :route_hash
268
- attr_reader :configuration_name
269
- attr_reader :configuration
270
- end
271
-
272
- def self.inherited(subclass)
273
- super
274
- subclass.instance_exec do
275
- @features = []
276
- @routes = []
277
- @route_hash = {}
278
- @configuration = Configuration.new(self)
279
- end
280
- end
281
-
282
- def self.configure(&block)
283
- @configuration.apply(&block)
284
- end
285
-
286
- def self.freeze
287
- @features.freeze
288
- @routes.freeze
289
- @route_hash.freeze
290
- super
291
- end
292
- end
293
-
294
275
  class Configuration
295
276
  attr_reader :auth
296
277
 
@@ -306,7 +287,6 @@ module Rodauth
306
287
  def apply(&block)
307
288
  load_feature(:base)
308
289
  instance_exec(&block)
309
- auth.allocate.post_configure
310
290
  end
311
291
 
312
292
  def enable(*features)
@@ -330,6 +310,46 @@ module Rodauth
330
310
  end
331
311
  end
332
312
 
313
+ class Auth
314
+ @features = []
315
+ @routes = []
316
+ @route_hash = {}
317
+ @configuration = Configuration.new(self)
318
+
319
+ class << self
320
+ attr_accessor :roda_class
321
+ attr_reader :features
322
+ attr_reader :routes
323
+ attr_accessor :route_hash
324
+ attr_reader :configuration_name
325
+ attr_reader :configuration
326
+ end
327
+
328
+ def self.inherited(subclass)
329
+ super
330
+ superclass = self
331
+ subclass.instance_exec do
332
+ @roda_class = superclass.roda_class
333
+ @features = superclass.features.clone
334
+ @routes = superclass.routes.clone
335
+ @route_hash = superclass.route_hash.clone
336
+ @configuration = superclass.configuration.clone
337
+ @configuration.instance_variable_set(:@auth, self)
338
+ end
339
+ end
340
+
341
+ def self.configure(&block)
342
+ @configuration.apply(&block)
343
+ end
344
+
345
+ def self.freeze
346
+ @features.freeze
347
+ @routes.freeze
348
+ @route_hash.freeze
349
+ super
350
+ end
351
+ end
352
+
333
353
  module InstanceMethods
334
354
  def rodauth(name=nil)
335
355
  if name
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.15.0
4
+ version: 2.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-27 00:00:00.000000000 Z
11
+ date: 2021-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -329,6 +329,10 @@ extra_rdoc_files:
329
329
  - doc/release_notes/2.13.0.txt
330
330
  - doc/release_notes/2.14.0.txt
331
331
  - doc/release_notes/2.15.0.txt
332
+ - doc/release_notes/2.16.0.txt
333
+ - doc/release_notes/2.17.0.txt
334
+ - doc/release_notes/2.18.0.txt
335
+ - doc/release_notes/2.19.0.txt
332
336
  - doc/release_notes/2.2.0.txt
333
337
  - doc/release_notes/2.3.0.txt
334
338
  - doc/release_notes/2.4.0.txt
@@ -361,6 +365,7 @@ files:
361
365
  - doc/guides/admin_activation.rdoc
362
366
  - doc/guides/already_authenticated.rdoc
363
367
  - doc/guides/alternative_login.rdoc
368
+ - doc/guides/change_table_and_column_names.rdoc
364
369
  - doc/guides/create_account_programmatically.rdoc
365
370
  - doc/guides/delay_password.rdoc
366
371
  - doc/guides/email_only.rdoc
@@ -378,6 +383,7 @@ files:
378
383
  - doc/guides/registration_field.rdoc
379
384
  - doc/guides/require_mfa.rdoc
380
385
  - doc/guides/reset_password_autologin.rdoc
386
+ - doc/guides/share_configuration.rdoc
381
387
  - doc/guides/status_column.rdoc
382
388
  - doc/guides/totp_or_recovery.rdoc
383
389
  - doc/http_basic_auth.rdoc
@@ -429,6 +435,10 @@ files:
429
435
  - doc/release_notes/2.13.0.txt
430
436
  - doc/release_notes/2.14.0.txt
431
437
  - doc/release_notes/2.15.0.txt
438
+ - doc/release_notes/2.16.0.txt
439
+ - doc/release_notes/2.17.0.txt
440
+ - doc/release_notes/2.18.0.txt
441
+ - doc/release_notes/2.19.0.txt
432
442
  - doc/release_notes/2.2.0.txt
433
443
  - doc/release_notes/2.3.0.txt
434
444
  - doc/release_notes/2.4.0.txt
@@ -560,7 +570,7 @@ metadata:
560
570
  bug_tracker_uri: https://github.com/jeremyevans/rodauth/issues
561
571
  changelog_uri: http://rodauth.jeremyevans.net/rdoc/files/CHANGELOG.html
562
572
  documentation_uri: http://rodauth.jeremyevans.net/documentation.html
563
- mailing_list_uri: https://groups.google.com/forum/#!forum/rodauth
573
+ mailing_list_uri: https://github.com/jeremyevans/rodauth/discussions
564
574
  source_code_uri: https://github.com/jeremyevans/rodauth
565
575
  post_install_message:
566
576
  rdoc_options:
@@ -584,7 +594,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
584
594
  - !ruby/object:Gem::Version
585
595
  version: '0'
586
596
  requirements: []
587
- rubygems_version: 3.2.22
597
+ rubygems_version: 3.2.32
588
598
  signing_key:
589
599
  specification_version: 4
590
600
  summary: Authentication and Account Management Framework for Rack Applications