rodauth 2.43.0 → 2.45.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.
- checksums.yaml +4 -4
- data/lib/rodauth/features/active_sessions.rb +32 -0
- data/lib/rodauth/features/argon2.rb +2 -0
- data/lib/rodauth/features/base.rb +52 -11
- data/lib/rodauth/features/disallow_password_reuse.rb +2 -0
- data/lib/rodauth/features/email_auth.rb +2 -0
- data/lib/rodauth/features/http_basic_auth.rb +8 -3
- data/lib/rodauth/features/internal_request.rb +18 -3
- data/lib/rodauth/features/json.rb +6 -2
- data/lib/rodauth/features/jwt.rb +6 -4
- data/lib/rodauth/features/jwt_refresh.rb +2 -0
- data/lib/rodauth/features/lockout.rb +10 -1
- data/lib/rodauth/features/login.rb +12 -2
- data/lib/rodauth/features/login_password_requirements_base.rb +2 -0
- data/lib/rodauth/features/otp.rb +8 -6
- data/lib/rodauth/features/otp_unlock.rb +2 -0
- data/lib/rodauth/features/password_grace_period.rb +3 -1
- data/lib/rodauth/features/password_pepper.rb +2 -0
- data/lib/rodauth/features/path_class_methods.rb +2 -2
- data/lib/rodauth/features/recovery_codes.rb +4 -2
- data/lib/rodauth/features/remember.rb +3 -1
- data/lib/rodauth/features/reset_password.rb +7 -0
- data/lib/rodauth/features/sms_codes.rb +15 -7
- data/lib/rodauth/features/two_factor_base.rb +2 -0
- data/lib/rodauth/features/update_password_hash.rb +2 -0
- data/lib/rodauth/features/verify_account.rb +2 -0
- data/lib/rodauth/features/verify_login_change.rb +2 -0
- data/lib/rodauth/features/webauthn_login.rb +2 -0
- data/lib/rodauth/features/webauthn_verify_account.rb +2 -0
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +111 -12
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5dea248f50448b260c6d0007d8bc4054f17794270e857a6310b969ba58105fbf
|
|
4
|
+
data.tar.gz: 875dbda4ddd85b04041a8e854e0adb09b466269ecef74dd1a73f384519d54553
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbf7c82f8b1c93496c59225891c1d747f317eaf46d8283f9c6f2d0562a48aaaec4d6491ed7567011356914fb4498e0f86576c230e183cf53c0ac7a7c5f27d99a
|
|
7
|
+
data.tar.gz: c8c2a228ce0395e4e14dbd1a69b01f7038b2ac94267b33bc5695d11b3eae6a278846be5100714dc18b0a65010db1693c0621f9ec1f2d0ba0793e92b61a698bf9
|
|
@@ -37,6 +37,8 @@ module Rodauth
|
|
|
37
37
|
:remove_inactive_sessions,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
uses_instance_variables(:@active_sessions_key, :@clear_active_sessions_after_two_factor_setup)
|
|
41
|
+
|
|
40
42
|
def currently_active_session?
|
|
41
43
|
return false unless session_id = session[session_id_session_key]
|
|
42
44
|
|
|
@@ -148,6 +150,21 @@ module Rodauth
|
|
|
148
150
|
remove_all_active_sessions
|
|
149
151
|
end
|
|
150
152
|
|
|
153
|
+
def after_otp_setup
|
|
154
|
+
super if defined?(super)
|
|
155
|
+
remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def after_sms_confirm
|
|
159
|
+
super if defined?(super)
|
|
160
|
+
remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def after_webauthn_setup
|
|
164
|
+
super if defined?(super)
|
|
165
|
+
remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup
|
|
166
|
+
end
|
|
167
|
+
|
|
151
168
|
def before_logout
|
|
152
169
|
if param_or_nil(global_logout_param)
|
|
153
170
|
remove_remember_key(session_value) if respond_to?(:remove_remember_key)
|
|
@@ -158,6 +175,21 @@ module Rodauth
|
|
|
158
175
|
super
|
|
159
176
|
end
|
|
160
177
|
|
|
178
|
+
def before_otp_setup
|
|
179
|
+
@clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup?
|
|
180
|
+
super if defined?(super)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def before_sms_confirm
|
|
184
|
+
@clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup?
|
|
185
|
+
super if defined?(super)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def before_webauthn_setup
|
|
189
|
+
@clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup?
|
|
190
|
+
super if defined?(super)
|
|
191
|
+
end
|
|
192
|
+
|
|
161
193
|
attr_reader :active_sessions_key
|
|
162
194
|
|
|
163
195
|
def generate_active_sessions_key
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require
|
|
3
|
+
begin
|
|
4
|
+
require "rack/version"
|
|
5
|
+
# :nocov:
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require "rack"
|
|
8
|
+
else
|
|
9
|
+
if Rack.release >= '3'
|
|
10
|
+
require "rack/request"
|
|
11
|
+
require "rack/utils"
|
|
12
|
+
else
|
|
13
|
+
require "rack"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
# :nocov:
|
|
5
17
|
|
|
6
18
|
module Rodauth
|
|
7
19
|
Feature.define(:base, :Base) do
|
|
8
20
|
after 'login'
|
|
21
|
+
after 'no_matching_login'
|
|
9
22
|
after 'login_failure'
|
|
10
23
|
before 'login'
|
|
11
24
|
before 'login_attempt'
|
|
@@ -22,7 +35,7 @@ module Rodauth
|
|
|
22
35
|
auth_value_method :accounts_table, :accounts
|
|
23
36
|
auth_value_method :cache_templates, true
|
|
24
37
|
auth_value_method :check_csrf_block, nil
|
|
25
|
-
auth_value_method :check_csrf_opts,
|
|
38
|
+
auth_value_method :check_csrf_opts, OPTS
|
|
26
39
|
auth_value_method :default_redirect, '/'
|
|
27
40
|
auth_value_method :convert_token_id_to_integer?, nil
|
|
28
41
|
flash_key :flash_error_key, :error
|
|
@@ -60,9 +73,10 @@ module Rodauth
|
|
|
60
73
|
auth_value_method :mark_input_fields_with_inputmode?, true
|
|
61
74
|
auth_value_method :skip_status_checks?, true
|
|
62
75
|
translatable_method :strftime_format, '%F %T'
|
|
63
|
-
auth_value_method :template_opts,
|
|
76
|
+
auth_value_method :template_opts, OPTS
|
|
64
77
|
auth_value_method :title_instance_variable, nil
|
|
65
78
|
auth_value_method :token_separator, "_"
|
|
79
|
+
auth_value_method :transaction_opts, OPTS
|
|
66
80
|
auth_value_method :unmatched_field_error_status, 422
|
|
67
81
|
auth_value_method :unopen_account_error_status, 403
|
|
68
82
|
translatable_method :unverified_account_message, "unverified account, please verify account before logging in"
|
|
@@ -94,6 +108,7 @@ module Rodauth
|
|
|
94
108
|
:check_csrf,
|
|
95
109
|
:clear_session,
|
|
96
110
|
:clear_tokens,
|
|
111
|
+
:convert_token_id,
|
|
97
112
|
:csrf_tag,
|
|
98
113
|
:function_name,
|
|
99
114
|
:hook_action,
|
|
@@ -122,7 +137,6 @@ module Rodauth
|
|
|
122
137
|
:account_from_id,
|
|
123
138
|
:account_from_login,
|
|
124
139
|
:account_from_session,
|
|
125
|
-
:convert_token_id,
|
|
126
140
|
:field_attributes,
|
|
127
141
|
:field_error_attributes,
|
|
128
142
|
:formatted_field_error,
|
|
@@ -137,14 +151,29 @@ module Rodauth
|
|
|
137
151
|
def auth_class_eval(&block)
|
|
138
152
|
auth.class_eval(&block)
|
|
139
153
|
end
|
|
154
|
+
|
|
155
|
+
def uses_instance_variables(*ivs)
|
|
156
|
+
auth.define_singleton_method(:instance_variables_used) do
|
|
157
|
+
super() + ivs
|
|
158
|
+
end
|
|
159
|
+
end
|
|
140
160
|
end
|
|
141
161
|
|
|
162
|
+
uses_instance_variables(
|
|
163
|
+
:@account,
|
|
164
|
+
:@current_route,
|
|
165
|
+
:@field_errors,
|
|
166
|
+
:@password_field_autocomplete_value,
|
|
167
|
+
:@has_password
|
|
168
|
+
)
|
|
169
|
+
|
|
142
170
|
attr_reader :scope
|
|
143
171
|
attr_reader :account
|
|
144
172
|
attr_reader :current_route
|
|
145
173
|
|
|
146
174
|
def initialize(scope)
|
|
147
175
|
@scope = scope
|
|
176
|
+
_initialize_instance_variables
|
|
148
177
|
end
|
|
149
178
|
|
|
150
179
|
def features
|
|
@@ -190,7 +219,7 @@ module Rodauth
|
|
|
190
219
|
end
|
|
191
220
|
end
|
|
192
221
|
|
|
193
|
-
def input_field_string(param, id, opts=
|
|
222
|
+
def input_field_string(param, id, opts=OPTS)
|
|
194
223
|
type = opts.fetch(:type, "text")
|
|
195
224
|
|
|
196
225
|
unless type == "password"
|
|
@@ -421,7 +450,7 @@ module Rodauth
|
|
|
421
450
|
_template_opts(opts, 'button')
|
|
422
451
|
end
|
|
423
452
|
|
|
424
|
-
def button(value, opts=
|
|
453
|
+
def button(value, opts=OPTS)
|
|
425
454
|
scope.render(button_opts(value, opts))
|
|
426
455
|
end
|
|
427
456
|
|
|
@@ -549,13 +578,16 @@ module Rodauth
|
|
|
549
578
|
end
|
|
550
579
|
|
|
551
580
|
def has_password?
|
|
552
|
-
return @has_password
|
|
581
|
+
return @has_password unless @has_password.nil?
|
|
553
582
|
return false unless account || session_value
|
|
554
583
|
@has_password = !!get_password_hash
|
|
555
584
|
end
|
|
556
585
|
|
|
557
586
|
private
|
|
558
587
|
|
|
588
|
+
def _initialize_instance_variables
|
|
589
|
+
end
|
|
590
|
+
|
|
559
591
|
def _around_rodauth
|
|
560
592
|
yield
|
|
561
593
|
end
|
|
@@ -665,17 +697,18 @@ module Rodauth
|
|
|
665
697
|
request.halt
|
|
666
698
|
end
|
|
667
699
|
|
|
668
|
-
def route_path(route, opts=
|
|
700
|
+
def route_path(route, opts=OPTS)
|
|
669
701
|
path = "#{prefix}/#{route}"
|
|
670
702
|
path += "?#{Rack::Utils.build_nested_query(opts)}" unless opts.empty?
|
|
671
703
|
path
|
|
672
704
|
end
|
|
673
705
|
|
|
674
|
-
def route_url(route, opts=
|
|
706
|
+
def route_url(route, opts=OPTS)
|
|
675
707
|
"#{base_url}#{route_path(route, opts)}"
|
|
676
708
|
end
|
|
677
709
|
|
|
678
|
-
def transaction(opts=
|
|
710
|
+
def transaction(opts=OPTS, &block)
|
|
711
|
+
opts = opts.empty? ? transaction_opts : transaction_opts.merge(opts)
|
|
679
712
|
db.transaction(opts, &block)
|
|
680
713
|
end
|
|
681
714
|
|
|
@@ -935,6 +968,14 @@ module Rodauth
|
|
|
935
968
|
false
|
|
936
969
|
end
|
|
937
970
|
|
|
971
|
+
# :nocov:
|
|
972
|
+
if RUBY_VERSION >= '4.0'
|
|
973
|
+
# :nocov:
|
|
974
|
+
def instance_variables_to_inspect
|
|
975
|
+
instance_variables.reject{|v| instance_variable_get(v).nil?}
|
|
976
|
+
end
|
|
977
|
+
end
|
|
978
|
+
|
|
938
979
|
def use_scope_clear_session?
|
|
939
980
|
scope.respond_to?(:clear_session)
|
|
940
981
|
end
|
|
@@ -5,10 +5,12 @@ module Rodauth
|
|
|
5
5
|
auth_value_method :http_basic_auth_realm, "protected"
|
|
6
6
|
auth_value_method :require_http_basic_auth?, false
|
|
7
7
|
|
|
8
|
+
uses_instance_variables(:@checked_http_basic_auth)
|
|
9
|
+
|
|
8
10
|
def logged_in?
|
|
9
11
|
ret = super
|
|
10
12
|
|
|
11
|
-
if !ret &&
|
|
13
|
+
if !ret && @checked_http_basic_auth.nil?
|
|
12
14
|
http_basic_auth
|
|
13
15
|
ret = super
|
|
14
16
|
end
|
|
@@ -32,9 +34,11 @@ module Rodauth
|
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
def http_basic_auth
|
|
35
|
-
|
|
37
|
+
unless @checked_http_basic_auth.nil?
|
|
38
|
+
return (@checked_http_basic_auth ? true : nil)
|
|
39
|
+
end
|
|
36
40
|
|
|
37
|
-
@checked_http_basic_auth =
|
|
41
|
+
@checked_http_basic_auth = false
|
|
38
42
|
return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1])
|
|
39
43
|
|
|
40
44
|
username, password = token.unpack("m*").first.split(/:/, 2)
|
|
@@ -42,6 +46,7 @@ module Rodauth
|
|
|
42
46
|
|
|
43
47
|
catch_error do
|
|
44
48
|
unless account_from_login(username)
|
|
49
|
+
after_no_matching_login
|
|
45
50
|
throw_basic_auth_error(login_param, no_matching_login_message)
|
|
46
51
|
end
|
|
47
52
|
|
|
@@ -187,6 +187,7 @@ module Rodauth
|
|
|
187
187
|
end
|
|
188
188
|
|
|
189
189
|
def _set_internal_request_return_value(value)
|
|
190
|
+
@internal_request_return_value_set = true
|
|
190
191
|
@internal_request_return_value = value
|
|
191
192
|
end
|
|
192
193
|
|
|
@@ -219,7 +220,7 @@ module Rodauth
|
|
|
219
220
|
|
|
220
221
|
def _handle_internal_request_eval(_)
|
|
221
222
|
v = instance_eval(&internal_request_block)
|
|
222
|
-
_set_internal_request_return_value(v) unless
|
|
223
|
+
_set_internal_request_return_value(v) unless @internal_request_return_value_set
|
|
223
224
|
end
|
|
224
225
|
|
|
225
226
|
def _handle_account_id_for_login(_)
|
|
@@ -304,7 +305,20 @@ module Rodauth
|
|
|
304
305
|
end
|
|
305
306
|
|
|
306
307
|
module InternalRequestClassMethods
|
|
307
|
-
def
|
|
308
|
+
def instance_variables_used
|
|
309
|
+
super + [
|
|
310
|
+
:@session,
|
|
311
|
+
:@params,
|
|
312
|
+
:@flash,
|
|
313
|
+
:@internal_request_block,
|
|
314
|
+
:@internal_request_return_value,
|
|
315
|
+
:@internal_request_return_value_set,
|
|
316
|
+
:@error_reason,
|
|
317
|
+
:@return_false_on_error
|
|
318
|
+
]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def internal_request(route, opts=OPTS, &block)
|
|
308
322
|
opts = opts.dup
|
|
309
323
|
|
|
310
324
|
env = {
|
|
@@ -404,13 +418,14 @@ module Rodauth
|
|
|
404
418
|
|
|
405
419
|
internal_class.send(:extend, InternalRequestClassMethods)
|
|
406
420
|
internal_class.send(:include, InternalRequestMethods)
|
|
421
|
+
internal_class.send(:make_shape_friendly)
|
|
407
422
|
internal_class.allocate.post_configure
|
|
408
423
|
|
|
409
424
|
([:base] + internal_class.features).each do |feature_name|
|
|
410
425
|
feature = FEATURES[feature_name]
|
|
411
426
|
if meths = feature.internal_request_methods
|
|
412
427
|
meths.each do |name|
|
|
413
|
-
klass.define_singleton_method(name){|opts=
|
|
428
|
+
klass.define_singleton_method(name){|opts=OPTS, &block| internal_class.internal_request(name, opts, &block)}
|
|
414
429
|
end
|
|
415
430
|
end
|
|
416
431
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:json, :Json) do
|
|
5
|
+
self.allowed_undefined_configuration_methods = [:only_json?].freeze
|
|
6
|
+
|
|
5
7
|
translatable_method :json_not_accepted_error_message, 'Unsupported Accept header. Must accept "application/json" or compatible content type'
|
|
6
8
|
translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
|
|
7
9
|
auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
|
|
@@ -27,6 +29,8 @@ module Rodauth
|
|
|
27
29
|
|
|
28
30
|
auth_private_methods :json_response_body
|
|
29
31
|
|
|
32
|
+
uses_instance_variables(:@json_request)
|
|
33
|
+
|
|
30
34
|
def set_field_error(field, message)
|
|
31
35
|
return super unless use_json?
|
|
32
36
|
json_response[json_response_field_error_key] = [field, message]
|
|
@@ -53,8 +57,8 @@ module Rodauth
|
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
def json_request?
|
|
56
|
-
return @json_request
|
|
57
|
-
@json_request = request.content_type
|
|
60
|
+
return @json_request unless @json_request.nil?
|
|
61
|
+
@json_request = !(request.content_type !~ json_request_content_type_regexp)
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
def use_json?
|
data/lib/rodauth/features/jwt.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Rodauth
|
|
|
11
11
|
auth_value_method :jwt_algorithm, "HS256"
|
|
12
12
|
auth_value_method :jwt_authorization_ignore, /\A(?:Basic|Digest) /
|
|
13
13
|
auth_value_method :jwt_authorization_remove, /\ABearer:?\s+/
|
|
14
|
-
auth_value_method :jwt_decode_opts,
|
|
14
|
+
auth_value_method :jwt_decode_opts, OPTS
|
|
15
15
|
auth_value_method :jwt_old_secret, nil
|
|
16
16
|
auth_value_method :jwt_session_key, nil
|
|
17
17
|
auth_value_method :jwt_symbolize_deeply?, false
|
|
@@ -30,8 +30,10 @@ module Rodauth
|
|
|
30
30
|
|
|
31
31
|
def_deprecated_alias :json_check_accept?, :jwt_check_accept?
|
|
32
32
|
|
|
33
|
+
uses_instance_variables(:@session, :@jwt_token, :@jwt_payload)
|
|
34
|
+
|
|
33
35
|
def session
|
|
34
|
-
return @session if
|
|
36
|
+
return @session if @session
|
|
35
37
|
return super unless use_jwt?
|
|
36
38
|
|
|
37
39
|
s = {}
|
|
@@ -76,7 +78,7 @@ module Rodauth
|
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def jwt_token
|
|
79
|
-
return @jwt_token if
|
|
81
|
+
return @jwt_token if @jwt_token
|
|
80
82
|
|
|
81
83
|
if (v = request.env['HTTP_AUTHORIZATION']) && v !~ jwt_authorization_ignore
|
|
82
84
|
@jwt_token = v.sub(jwt_authorization_remove, '')
|
|
@@ -120,7 +122,7 @@ module Rodauth
|
|
|
120
122
|
end
|
|
121
123
|
|
|
122
124
|
def jwt_payload
|
|
123
|
-
return @jwt_payload
|
|
125
|
+
return @jwt_payload unless @jwt_payload.nil?
|
|
124
126
|
@jwt_payload = JWT.decode(jwt_token, _jwt_decode_secrets, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0]
|
|
125
127
|
rescue JWT::DecodeError => e
|
|
126
128
|
rescue_jwt_payload(e)
|
|
@@ -61,6 +61,8 @@ module Rodauth
|
|
|
61
61
|
)
|
|
62
62
|
auth_private_methods :account_from_unlock_key
|
|
63
63
|
|
|
64
|
+
uses_instance_variables(:@unlock_account_key_value)
|
|
65
|
+
|
|
64
66
|
internal_request_method(:lock_account)
|
|
65
67
|
internal_request_method(:unlock_account_request)
|
|
66
68
|
internal_request_method(:unlock_account)
|
|
@@ -249,7 +251,14 @@ module Rodauth
|
|
|
249
251
|
|
|
250
252
|
private
|
|
251
253
|
|
|
252
|
-
|
|
254
|
+
def unlock_account_key
|
|
255
|
+
@unlock_account_key_value
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def unlock_account_key_value
|
|
259
|
+
# RODAUTH3: call unlock_account_key directly
|
|
260
|
+
unlock_account_key
|
|
261
|
+
end
|
|
253
262
|
|
|
254
263
|
def before_login_attempt
|
|
255
264
|
if locked_out?
|
|
@@ -20,8 +20,8 @@ module Rodauth
|
|
|
20
20
|
|
|
21
21
|
session_key :login_redirect_session_key, :login_redirect
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
cached_auth_method :multi_phase_login_forms
|
|
24
|
+
cached_auth_method :login_form_footer
|
|
25
25
|
|
|
26
26
|
auth_value_methods :login_return_to_requested_location_path
|
|
27
27
|
|
|
@@ -30,6 +30,15 @@ module Rodauth
|
|
|
30
30
|
:login_response
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
+
uses_instance_variables(
|
|
34
|
+
:@login_form_footer,
|
|
35
|
+
:@login_form_footer_links,
|
|
36
|
+
:@login_form_header,
|
|
37
|
+
:@multi_phase_login_forms,
|
|
38
|
+
:@saved_login_redirect,
|
|
39
|
+
:@valid_login_entered
|
|
40
|
+
)
|
|
41
|
+
|
|
33
42
|
internal_request_method
|
|
34
43
|
internal_request_method :valid_login_and_password?
|
|
35
44
|
|
|
@@ -47,6 +56,7 @@ module Rodauth
|
|
|
47
56
|
|
|
48
57
|
catch_error do
|
|
49
58
|
unless account_from_login(login_param_value)
|
|
59
|
+
after_no_matching_login
|
|
50
60
|
throw_error_reason(:no_matching_login, no_matching_login_error_status, login_param, no_matching_login_message)
|
|
51
61
|
end
|
|
52
62
|
|
data/lib/rodauth/features/otp.rb
CHANGED
|
@@ -68,8 +68,8 @@ module Rodauth
|
|
|
68
68
|
auth_value_method :otp_setup_raw_param, 'otp_raw_secret'
|
|
69
69
|
translatable_method :otp_auth_form_footer, ''
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
cached_auth_method :otp_key
|
|
72
|
+
cached_auth_method :otp
|
|
73
73
|
private :otp
|
|
74
74
|
|
|
75
75
|
auth_value_methods(
|
|
@@ -100,6 +100,8 @@ module Rodauth
|
|
|
100
100
|
:otp_valid_code_for_old_secret
|
|
101
101
|
)
|
|
102
102
|
|
|
103
|
+
uses_instance_variables(:@otp, :@otp_key, :@otp_user_key, :@otp_tmp_key)
|
|
104
|
+
|
|
103
105
|
internal_request_method :otp_setup_params
|
|
104
106
|
internal_request_method :otp_setup
|
|
105
107
|
internal_request_method :otp_auth
|
|
@@ -245,7 +247,7 @@ module Rodauth
|
|
|
245
247
|
end
|
|
246
248
|
|
|
247
249
|
def otp_exists?
|
|
248
|
-
|
|
250
|
+
!!otp_key
|
|
249
251
|
end
|
|
250
252
|
|
|
251
253
|
def otp_valid_code?(ot_pass)
|
|
@@ -277,7 +279,7 @@ module Rodauth
|
|
|
277
279
|
|
|
278
280
|
def otp_remove
|
|
279
281
|
otp_key_ds.delete
|
|
280
|
-
@otp_key =
|
|
282
|
+
@otp_key = false
|
|
281
283
|
end
|
|
282
284
|
|
|
283
285
|
def otp_add_key
|
|
@@ -368,7 +370,7 @@ module Rodauth
|
|
|
368
370
|
end
|
|
369
371
|
|
|
370
372
|
def clear_cached_otp
|
|
371
|
-
|
|
373
|
+
@otp = nil
|
|
372
374
|
end
|
|
373
375
|
|
|
374
376
|
def otp_tmp_key(secret)
|
|
@@ -436,7 +438,7 @@ module Rodauth
|
|
|
436
438
|
|
|
437
439
|
def _otp_key
|
|
438
440
|
@otp_user_key = nil
|
|
439
|
-
otp_key_ds.get(otp_keys_column)
|
|
441
|
+
otp_key_ds.get(otp_keys_column) || false
|
|
440
442
|
end
|
|
441
443
|
|
|
442
444
|
def _otp_for_key(key)
|
|
@@ -7,6 +7,8 @@ module Rodauth
|
|
|
7
7
|
|
|
8
8
|
auth_methods :password_recently_entered?
|
|
9
9
|
|
|
10
|
+
uses_instance_variables(:@last_password_entry)
|
|
11
|
+
|
|
10
12
|
def modifications_require_password?
|
|
11
13
|
return false unless super
|
|
12
14
|
!password_recently_entered?
|
|
@@ -26,7 +28,7 @@ module Rodauth
|
|
|
26
28
|
|
|
27
29
|
def update_session
|
|
28
30
|
super
|
|
29
|
-
set_session_value(last_password_entry_session_key, @last_password_entry) if
|
|
31
|
+
set_session_value(last_password_entry_session_key, @last_password_entry) if @last_password_entry
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
private
|
|
@@ -8,6 +8,8 @@ module Rodauth
|
|
|
8
8
|
auth_value_method :previous_password_peppers, [""]
|
|
9
9
|
auth_value_method :password_pepper_update?, true
|
|
10
10
|
|
|
11
|
+
uses_instance_variables(:@previous_pepper_matched)
|
|
12
|
+
|
|
11
13
|
def password_match?(password)
|
|
12
14
|
if (result = super) && @previous_pepper_matched && password_pepper_update?
|
|
13
15
|
set_password(password)
|
|
@@ -13,8 +13,8 @@ module Rodauth
|
|
|
13
13
|
path_meth = :"#{route}_path"
|
|
14
14
|
url_meth = :"#{route}_url"
|
|
15
15
|
instance = klass.allocate.freeze
|
|
16
|
-
klass.define_singleton_method(path_meth){|opts=
|
|
17
|
-
klass.define_singleton_method(url_meth){|opts=
|
|
16
|
+
klass.define_singleton_method(path_meth){|opts=OPTS| instance.send(path_meth, opts)}
|
|
17
|
+
klass.define_singleton_method(url_meth){|opts=OPTS| instance.send(url_meth, opts)}
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -46,7 +46,7 @@ module Rodauth
|
|
|
46
46
|
translatable_method :recovery_auth_link_text, "Authenticate Using Recovery Code"
|
|
47
47
|
translatable_method :recovery_codes_link_text, "View Authentication Recovery Codes"
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
cached_auth_method :recovery_codes
|
|
50
50
|
|
|
51
51
|
auth_value_methods(
|
|
52
52
|
:recovery_codes_primary?
|
|
@@ -60,6 +60,8 @@ module Rodauth
|
|
|
60
60
|
:recovery_codes_available?,
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
uses_instance_variables(:@recovery_codes, :@recovery_codes_button)
|
|
64
|
+
|
|
63
65
|
internal_request_method :recovery_codes
|
|
64
66
|
internal_request_method :recovery_auth
|
|
65
67
|
internal_request_method :valid_recovery_auth?
|
|
@@ -181,7 +183,7 @@ module Rodauth
|
|
|
181
183
|
add_recovery_code
|
|
182
184
|
end
|
|
183
185
|
end
|
|
184
|
-
|
|
186
|
+
@recovery_codes = nil
|
|
185
187
|
end
|
|
186
188
|
|
|
187
189
|
def add_recovery_code
|
|
@@ -16,7 +16,7 @@ module Rodauth
|
|
|
16
16
|
response
|
|
17
17
|
|
|
18
18
|
auth_value_method :raw_remember_token_deadline, nil
|
|
19
|
-
auth_value_method :remember_cookie_options,
|
|
19
|
+
auth_value_method :remember_cookie_options, OPTS
|
|
20
20
|
auth_value_method :extend_remember_deadline?, false
|
|
21
21
|
auth_value_method :extend_remember_deadline_period, 3600
|
|
22
22
|
auth_value_method :remember_period, {:days=>14}.freeze
|
|
@@ -49,6 +49,8 @@ module Rodauth
|
|
|
49
49
|
:remove_remember_key
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
+
uses_instance_variables(:@remember_key_value)
|
|
53
|
+
|
|
52
54
|
internal_request_method :remember_setup
|
|
53
55
|
internal_request_method :remember_disable
|
|
54
56
|
internal_request_method :account_id_for_remember_key
|
|
@@ -57,6 +57,8 @@ module Rodauth
|
|
|
57
57
|
:account_from_reset_password_key
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
+
uses_instance_variables(:@reset_password_key_value)
|
|
61
|
+
|
|
60
62
|
internal_request_method(:reset_password_request)
|
|
61
63
|
internal_request_method
|
|
62
64
|
|
|
@@ -190,6 +192,11 @@ module Rodauth
|
|
|
190
192
|
end
|
|
191
193
|
|
|
192
194
|
def get_password_reset_key(id)
|
|
195
|
+
# RODAUTH3: Remove method and call get_reset_password_key directly
|
|
196
|
+
get_reset_password_key(id)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def get_reset_password_key(id)
|
|
193
200
|
ds = password_reset_ds(id)
|
|
194
201
|
ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete
|
|
195
202
|
ds.get(reset_password_key_column)
|
|
@@ -89,15 +89,13 @@ module Rodauth
|
|
|
89
89
|
auth_value_method :sms_phone_min_length, 7
|
|
90
90
|
auth_value_method :sms_phone_param, 'sms-phone'
|
|
91
91
|
|
|
92
|
-
auth_cached_method :sms
|
|
93
|
-
|
|
94
92
|
auth_value_methods(
|
|
95
93
|
:sms_codes_primary?,
|
|
96
94
|
:sms_needs_confirmation_notice_flash,
|
|
97
|
-
:sms_request_response
|
|
98
95
|
)
|
|
99
96
|
|
|
100
97
|
auth_methods(
|
|
98
|
+
:sms,
|
|
101
99
|
:sms_auth_message,
|
|
102
100
|
:sms_available?,
|
|
103
101
|
:sms_code_issued_at,
|
|
@@ -121,6 +119,9 @@ module Rodauth
|
|
|
121
119
|
:sms_setup?,
|
|
122
120
|
:sms_valid_phone?
|
|
123
121
|
)
|
|
122
|
+
auth_private_methods :sms_request_response
|
|
123
|
+
|
|
124
|
+
uses_instance_variables(:@sms)
|
|
124
125
|
|
|
125
126
|
internal_request_method :sms_setup
|
|
126
127
|
internal_request_method :sms_confirm
|
|
@@ -356,7 +357,7 @@ module Rodauth
|
|
|
356
357
|
|
|
357
358
|
def sms_disable
|
|
358
359
|
sms_ds.delete
|
|
359
|
-
@sms =
|
|
360
|
+
@sms = false
|
|
360
361
|
end
|
|
361
362
|
|
|
362
363
|
def sms_confirm_failure
|
|
@@ -367,7 +368,7 @@ module Rodauth
|
|
|
367
368
|
# Cannot handle uniqueness violation here, as the phone number given may not match the
|
|
368
369
|
# one in the table.
|
|
369
370
|
sms_ds.insert(sms_id_column=>session_value, sms_phone_column=>phone_number, sms_failures_column => nil)
|
|
370
|
-
|
|
371
|
+
@sms = nil
|
|
371
372
|
end
|
|
372
373
|
|
|
373
374
|
def sms_remove_failures
|
|
@@ -521,8 +522,15 @@ module Rodauth
|
|
|
521
522
|
update_hash_ds(sms, sms_ds, values)
|
|
522
523
|
end
|
|
523
524
|
|
|
524
|
-
def
|
|
525
|
-
|
|
525
|
+
def sms
|
|
526
|
+
case @sms
|
|
527
|
+
when nil
|
|
528
|
+
(@sms = sms_ds.first || false) || nil
|
|
529
|
+
when false
|
|
530
|
+
nil
|
|
531
|
+
else
|
|
532
|
+
@sms
|
|
533
|
+
end
|
|
526
534
|
end
|
|
527
535
|
|
|
528
536
|
def sms_ds
|
|
@@ -62,6 +62,8 @@ module Rodauth
|
|
|
62
62
|
:two_factor_remove_links
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
+
uses_instance_variables(:@two_factor_auth_links, :@two_factor_setup_links, :@two_factor_remove_links)
|
|
66
|
+
|
|
65
67
|
internal_request_method :two_factor_disable
|
|
66
68
|
|
|
67
69
|
route(:two_factor_manage, 'multifactor-manage') do |r|
|
|
@@ -4,6 +4,8 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:update_password_hash, :UpdatePasswordHash) do
|
|
5
5
|
depends :login_password_requirements_base
|
|
6
6
|
|
|
7
|
+
uses_instance_variables(:@current_password_hash_cost, :@update_password_hash)
|
|
8
|
+
|
|
7
9
|
def password_match?(password)
|
|
8
10
|
if (result = super) && update_password_hash?
|
|
9
11
|
@update_password_hash = false
|
data/lib/rodauth/version.rb
CHANGED
data/lib/rodauth.rb
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
|
|
5
5
|
module Rodauth
|
|
6
|
+
OPTS = {}.freeze
|
|
7
|
+
SCOPE_INSTANCE_VARIABLES = [:@_rodauths, :@_rodauth].freeze
|
|
8
|
+
|
|
6
9
|
class ConfigurationError < StandardError; end
|
|
7
10
|
|
|
8
|
-
def self.lib(opts=
|
|
11
|
+
def self.lib(opts=OPTS, &block)
|
|
9
12
|
require 'roda'
|
|
10
13
|
c = Class.new(Roda)
|
|
11
14
|
c.plugin(:rodauth, opts) do
|
|
@@ -16,7 +19,7 @@ module Rodauth
|
|
|
16
19
|
c.rodauth
|
|
17
20
|
end
|
|
18
21
|
|
|
19
|
-
def self.load_dependencies(app, opts=
|
|
22
|
+
def self.load_dependencies(app, opts=OPTS, &_)
|
|
20
23
|
json_opt = opts.fetch(:json, app.opts[:rodauth_json])
|
|
21
24
|
if json_opt
|
|
22
25
|
app.plugin :json
|
|
@@ -45,7 +48,7 @@ module Rodauth
|
|
|
45
48
|
end
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
def self.configure(app, opts=
|
|
51
|
+
def self.configure(app, opts=OPTS, &block)
|
|
49
52
|
json_opt = app.opts[:rodauth_json] = opts.fetch(:json, app.opts[:rodauth_json])
|
|
50
53
|
csrf = app.opts[:rodauth_csrf] = opts.fetch(:csrf, app.opts[:rodauth_csrf])
|
|
51
54
|
app.opts[:rodauth_route_csrf] = case csrf
|
|
@@ -63,6 +66,7 @@ module Rodauth
|
|
|
63
66
|
end
|
|
64
67
|
auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)}
|
|
65
68
|
auth_class.configure(&block) if block
|
|
69
|
+
auth_class.send(:make_shape_friendly)
|
|
66
70
|
auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure)
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -70,15 +74,32 @@ module Rodauth
|
|
|
70
74
|
|
|
71
75
|
class FeatureConfiguration < Module
|
|
72
76
|
def def_configuration_methods(feature)
|
|
73
|
-
private_methods = feature.private_instance_methods
|
|
77
|
+
private_methods = feature.private_instance_methods
|
|
74
78
|
priv = proc{|m| private_methods.include?(m)}
|
|
75
|
-
feature.auth_methods.each
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
feature.auth_methods.each do |m|
|
|
80
|
+
_check_method_defined(feature, m, priv[m])
|
|
81
|
+
def_auth_method(m, priv[m])
|
|
82
|
+
end
|
|
83
|
+
feature.auth_value_methods.each do |m|
|
|
84
|
+
_check_method_defined(feature, m, priv[m])
|
|
85
|
+
def_auth_value_method(m, priv[m])
|
|
86
|
+
end
|
|
87
|
+
feature.auth_private_methods.each do |m|
|
|
88
|
+
_check_method_defined(feature, :"_#{m}", true)
|
|
89
|
+
def_auth_private_method(m)
|
|
90
|
+
end
|
|
78
91
|
end
|
|
79
92
|
|
|
80
93
|
private
|
|
81
94
|
|
|
95
|
+
def _check_method_defined(feature, meth, priv)
|
|
96
|
+
if !feature.send(priv ? :private_method_defined? : :method_defined?, meth) &&
|
|
97
|
+
(!(allowed = feature.allowed_undefined_configuration_methods) || !allowed.include?(meth))
|
|
98
|
+
# RODAUTH3: raise instead of warn
|
|
99
|
+
warn "Bug in Rodauth #{feature.feature_name} feature definition, configuration method added for #{meth}, but the feature doesn't define the method"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
82
103
|
def def_auth_method(meth, priv)
|
|
83
104
|
define_method(meth) do |&block|
|
|
84
105
|
@auth.send(:define_method, meth, &block)
|
|
@@ -124,14 +145,16 @@ module Rodauth
|
|
|
124
145
|
attr_accessor :dependencies
|
|
125
146
|
attr_accessor :routes
|
|
126
147
|
attr_accessor :configuration
|
|
148
|
+
attr_accessor :allowed_undefined_configuration_methods
|
|
127
149
|
attr_reader :internal_request_methods
|
|
150
|
+
attr_reader :instance_variables_used
|
|
128
151
|
|
|
129
152
|
def route(name=feature_name, default=name.to_s.tr('_', '-'), &block)
|
|
130
153
|
route_meth = :"#{name}_route"
|
|
131
154
|
auth_value_method route_meth, default
|
|
132
155
|
|
|
133
|
-
define_method(:"#{name}_path"){|opts=
|
|
134
|
-
define_method(:"#{name}_url"){|opts=
|
|
156
|
+
define_method(:"#{name}_path"){|opts=OPTS| route_path(send(route_meth), opts) if send(route_meth)}
|
|
157
|
+
define_method(:"#{name}_url"){|opts=OPTS| route_url(send(route_meth), opts) if send(route_meth)}
|
|
135
158
|
|
|
136
159
|
handle_meth = :"handle_#{name}"
|
|
137
160
|
internal_handle_meth = :"_#{handle_meth}"
|
|
@@ -175,6 +198,10 @@ module Rodauth
|
|
|
175
198
|
(@internal_request_methods ||= []) << name
|
|
176
199
|
end
|
|
177
200
|
|
|
201
|
+
def uses_instance_variables(*ivs)
|
|
202
|
+
@instance_variables_used = ivs.freeze
|
|
203
|
+
end
|
|
204
|
+
|
|
178
205
|
def configuration_module_eval(&block)
|
|
179
206
|
configuration.module_eval(&block)
|
|
180
207
|
end
|
|
@@ -254,7 +281,7 @@ module Rodauth
|
|
|
254
281
|
end
|
|
255
282
|
end
|
|
256
283
|
|
|
257
|
-
def email(type, subject, opts =
|
|
284
|
+
def email(type, subject, opts = OPTS)
|
|
258
285
|
subject_method = :"#{type}_email_subject"
|
|
259
286
|
body_method = :"#{type}_email_body"
|
|
260
287
|
create_method = :"create_#{type}_email"
|
|
@@ -305,7 +332,24 @@ module Rodauth
|
|
|
305
332
|
auth_value_methods(meth)
|
|
306
333
|
end
|
|
307
334
|
|
|
308
|
-
|
|
335
|
+
# Auth caching method that treats a nil instance variable as
|
|
336
|
+
# not being cached. If nil is a valid value for the instance
|
|
337
|
+
# variable, do not use this method, use a regular auth_method
|
|
338
|
+
# and handle caching manually.
|
|
339
|
+
def cached_auth_method(meth, iv=:"@#{meth}")
|
|
340
|
+
umeth = :"_#{meth}"
|
|
341
|
+
define_method(meth) do
|
|
342
|
+
v = instance_variable_get(iv)
|
|
343
|
+
v.nil? ? instance_variable_set(iv, send(umeth)) : v
|
|
344
|
+
end
|
|
345
|
+
alias_method(meth, meth)
|
|
346
|
+
auth_private_methods(meth)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# :nocov:
|
|
350
|
+
def auth_cached_method(meth, iv=:"@#{meth}") # :nodoc:
|
|
351
|
+
# Non-shape friendly historical method.
|
|
352
|
+
# RODAUTH3: Remove
|
|
309
353
|
umeth = :"_#{meth}"
|
|
310
354
|
define_method(meth) do
|
|
311
355
|
if instance_variable_defined?(iv)
|
|
@@ -317,6 +361,7 @@ module Rodauth
|
|
|
317
361
|
alias_method(meth, meth)
|
|
318
362
|
auth_private_methods(meth)
|
|
319
363
|
end
|
|
364
|
+
# :nocov:
|
|
320
365
|
|
|
321
366
|
[:notice_flash, :error_flash, :button].each do |meth|
|
|
322
367
|
define_method(meth) do |v, name=feature_name|
|
|
@@ -376,6 +421,60 @@ module Rodauth
|
|
|
376
421
|
attr_accessor :route_hash
|
|
377
422
|
attr_reader :configuration_name
|
|
378
423
|
attr_reader :configuration
|
|
424
|
+
|
|
425
|
+
private
|
|
426
|
+
|
|
427
|
+
if RUBY_VERSION >= "3.2" && defined?(RubyVM::YJIT.enable)
|
|
428
|
+
# Use a shape-friendly object on Ruby 3.2 if YJIT is available
|
|
429
|
+
# with the assumption that it will be enabled later in any situation
|
|
430
|
+
# desiring high performance.
|
|
431
|
+
def make_shape_friendly
|
|
432
|
+
ivs = instance_variables_used
|
|
433
|
+
|
|
434
|
+
unless ivs.empty?
|
|
435
|
+
ivs.uniq!
|
|
436
|
+
ivs = ivs.reverse.join(" = ")
|
|
437
|
+
method_content = "#{ivs} = nil"
|
|
438
|
+
class_eval(<<-RUBY, __FILE__, __LINE__+1)
|
|
439
|
+
private def _initialize_instance_variables
|
|
440
|
+
#{method_content}
|
|
441
|
+
end
|
|
442
|
+
alias _initialize_instance_variables _initialize_instance_variables
|
|
443
|
+
RUBY
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
nil
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def instance_variables_used
|
|
450
|
+
ivs = []
|
|
451
|
+
features = []
|
|
452
|
+
|
|
453
|
+
# Try to ensure that instance variable order follows feature enablement order
|
|
454
|
+
ancestors.each do |mod|
|
|
455
|
+
features << mod if mod.is_a?(Feature)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
features.reverse_each do |mod|
|
|
459
|
+
if (feature_ivs = mod.instance_variables_used)
|
|
460
|
+
feature_ivs.each do |iv|
|
|
461
|
+
unless iv.match?(/\A@[a-z_][a-z0-9_]*\z/)
|
|
462
|
+
raise ConfigurationError, "invalid model instance variable used"
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
ivs << iv
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
ivs
|
|
471
|
+
end
|
|
472
|
+
# :nocov:
|
|
473
|
+
else
|
|
474
|
+
def make_shape_friendly # :nodoc:
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
# :nocov:
|
|
379
478
|
end
|
|
380
479
|
|
|
381
480
|
def self.inherited(subclass)
|
|
@@ -429,7 +528,7 @@ module Rodauth
|
|
|
429
528
|
view_opts = rodauth.send(:loaded_templates).map do |page|
|
|
430
529
|
rodauth.send(:_view_opts, page)
|
|
431
530
|
end
|
|
432
|
-
view_opts << rodauth.send(:button_opts, '',
|
|
531
|
+
view_opts << rodauth.send(:button_opts, '', OPTS)
|
|
433
532
|
|
|
434
533
|
view_opts.each do |opts|
|
|
435
534
|
instance.send(:retrieve_template, opts).send(:compiled_method, opts[:locals].keys.sort_by(&:to_s))
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rodauth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.45.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
@@ -403,7 +403,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
403
403
|
- !ruby/object:Gem::Version
|
|
404
404
|
version: '0'
|
|
405
405
|
requirements: []
|
|
406
|
-
rubygems_version: 4.0.
|
|
406
|
+
rubygems_version: 4.0.10
|
|
407
407
|
specification_version: 4
|
|
408
408
|
summary: Authentication and Account Management Framework for Rack Applications
|
|
409
409
|
test_files: []
|