noresponse 2.1.13 → 2.1.14
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/no_response/late_results.rb +114 -14
- data/lib/no_response/sdkconfiguration.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9c7ea57323988c4c7426888734ef4c533166f83c713af0005ecfcb6d3a3ee43
|
|
4
|
+
data.tar.gz: f61a863f292e168cd2e6505479fb77346057088300d72f164b74a015647b0efc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb0dc8a6a3469249db874f6fc187e8697b2ce7eb0b921231e6447be07578d6484ea9b302fa03b723be8219711bd2f53889ab3d2d95233bad9826bb6d43cfac6a
|
|
7
|
+
data.tar.gz: 8b7bc769f6a8c3c961c5180e3707a39896af75047491a480e70a744e2fd0931aeaa44062ce705e995b9229b1e2ef1d05d0891841ea4959fc21e5cd4a56e003b9
|
|
@@ -39,17 +39,23 @@ module NoResponse
|
|
|
39
39
|
# with a 5xx so NoResponse retries it. The message holds no response body
|
|
40
40
|
# and no credentials.
|
|
41
41
|
class ProviderActionError < StandardError
|
|
42
|
-
|
|
42
|
+
# code is the provider's error code from the response body, such as
|
|
43
|
+
# Auth0's errorCode "insufficient_scope", when it sent one.
|
|
44
|
+
attr_reader :provider, :status, :code
|
|
43
45
|
|
|
44
|
-
def initialize(provider, status, message)
|
|
46
|
+
def initialize(provider, status, message, code = nil)
|
|
45
47
|
super(message)
|
|
46
48
|
@provider = provider
|
|
47
49
|
@status = status
|
|
50
|
+
@code = code
|
|
48
51
|
end
|
|
49
52
|
end
|
|
50
53
|
|
|
51
|
-
# event is the verified Webhooks::Event. action is the action
|
|
52
|
-
# when the policy, the event or the user id called for nothing.
|
|
54
|
+
# event is the verified Webhooks::Event. action is the action that ran:
|
|
55
|
+
# NONE when the policy, the event or the user id called for nothing. It can
|
|
56
|
+
# be stronger than the policy's action when the provider had to fall back:
|
|
57
|
+
# the auth0 provider reports BAN when it blocked the user because the
|
|
58
|
+
# tenant's plan cannot end sessions. user_id is
|
|
53
59
|
# the provider user id acted on, set only when an action was taken. Answer
|
|
54
60
|
# an endpoint.verification with Webhooks.verification_response(event).
|
|
55
61
|
Result = Struct.new(:event, :action, :user_id, keyword_init: true)
|
|
@@ -102,12 +108,17 @@ module NoResponse
|
|
|
102
108
|
return Result.new(event: event, action: NONE) if resolved.nil? || resolved.to_s.empty?
|
|
103
109
|
|
|
104
110
|
resolved = resolved.to_s
|
|
105
|
-
|
|
111
|
+
# A provider may define act(action, user_id), returning the action that
|
|
112
|
+
# ran; the auth0 provider does, to report a block it had to fall back to.
|
|
113
|
+
ran = action
|
|
114
|
+
if provider.respond_to?(:act)
|
|
115
|
+
ran = provider.act(action, resolved)
|
|
116
|
+
elsif action == BAN
|
|
106
117
|
provider.ban(resolved)
|
|
107
118
|
else
|
|
108
119
|
provider.revoke_sessions(resolved)
|
|
109
120
|
end
|
|
110
|
-
Result.new(event: event, action: action, user_id: resolved)
|
|
121
|
+
Result.new(event: event, action: ACTIONS.include?(ran) ? ran : action, user_id: resolved)
|
|
111
122
|
end
|
|
112
123
|
|
|
113
124
|
# Clerk Backend API. revoke_sessions lists the user's active sessions
|
|
@@ -146,8 +157,18 @@ module NoResponse
|
|
|
146
157
|
# (PATCH /users/{id}), then does the same. Pass domain (such as
|
|
147
158
|
# example.eu.auth0.com; the API is https://{domain}/api/v2) or base_url.
|
|
148
159
|
# token is a Management API access token with update:users,
|
|
149
|
-
# delete:sessions
|
|
150
|
-
#
|
|
160
|
+
# delete:sessions, delete:refresh_tokens, read:device_credentials and
|
|
161
|
+
# delete:device_credentials. A 404 on any call (user already gone)
|
|
162
|
+
# counts as done, so a repeated delivery is safe.
|
|
163
|
+
#
|
|
164
|
+
# Auth0 offers the sessions and refresh-token endpoints on Enterprise
|
|
165
|
+
# plans only; on other plans they answer 403 feature_not_enabled. Then
|
|
166
|
+
# refresh tokens are deleted one by one through /device-credentials
|
|
167
|
+
# (types refresh_token and rotating_refresh_token), which every plan
|
|
168
|
+
# has. A login session cannot be ended, so revoke_sessions blocks the
|
|
169
|
+
# user before deleting the refresh tokens, and act reports BAN. Unblock
|
|
170
|
+
# the user in the dashboard to undo it. Any other refusal, such as 403
|
|
171
|
+
# insufficient_scope, raises.
|
|
151
172
|
def auth0(token:, domain: nil, base_url: nil, http_client: nil)
|
|
152
173
|
raise ArgumentError, 'auth0 needs token' if token.to_s.empty?
|
|
153
174
|
|
|
@@ -191,7 +212,9 @@ module NoResponse
|
|
|
191
212
|
end
|
|
192
213
|
status, text = perform(method, uri, headers, payload)
|
|
193
214
|
return nil if allow_404 && status == 404
|
|
194
|
-
|
|
215
|
+
unless status.between?(200, 299)
|
|
216
|
+
raise ProviderActionError.new(@name, status, "#{@name} #{method} #{path} answered #{status}", error_code(text))
|
|
217
|
+
end
|
|
195
218
|
return nil if text.nil? || text.empty?
|
|
196
219
|
|
|
197
220
|
begin
|
|
@@ -203,6 +226,17 @@ module NoResponse
|
|
|
203
226
|
|
|
204
227
|
private
|
|
205
228
|
|
|
229
|
+
# The error code in a provider's error body: Auth0's errorCode, or a top-level code.
|
|
230
|
+
def error_code(text)
|
|
231
|
+
body = JSON.parse(text.to_s)
|
|
232
|
+
return nil unless body.is_a?(Hash)
|
|
233
|
+
|
|
234
|
+
code = body['errorCode'] || body['code']
|
|
235
|
+
code.is_a?(String) && !code.empty? ? code : nil
|
|
236
|
+
rescue JSON::ParserError
|
|
237
|
+
nil
|
|
238
|
+
end
|
|
239
|
+
|
|
206
240
|
def perform(method, uri, headers, payload)
|
|
207
241
|
return @http_client.call(method, uri, headers, payload) if @http_client
|
|
208
242
|
|
|
@@ -296,6 +330,8 @@ module NoResponse
|
|
|
296
330
|
end
|
|
297
331
|
|
|
298
332
|
class Auth0
|
|
333
|
+
PAGE = 100
|
|
334
|
+
|
|
299
335
|
def initialize(caller_)
|
|
300
336
|
@call = caller_
|
|
301
337
|
end
|
|
@@ -303,15 +339,79 @@ module NoResponse
|
|
|
303
339
|
def name = 'auth0'
|
|
304
340
|
|
|
305
341
|
def revoke_sessions(user_id)
|
|
306
|
-
|
|
307
|
-
@call.call('DELETE', "/users/#{user}/sessions", allow_404: true)
|
|
308
|
-
@call.call('DELETE', "/users/#{user}/refresh-tokens", allow_404: true)
|
|
342
|
+
revoke(user_id)
|
|
309
343
|
nil
|
|
310
344
|
end
|
|
311
345
|
|
|
312
346
|
def ban(user_id)
|
|
313
|
-
|
|
314
|
-
|
|
347
|
+
user = Segment.encode(user_id)
|
|
348
|
+
block(user)
|
|
349
|
+
# The block already ends a session the plan cannot delete.
|
|
350
|
+
delete_sessions(user)
|
|
351
|
+
delete_refresh_tokens(user_id, user)
|
|
352
|
+
nil
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Bans or revokes and returns the action that ran: BAN when revoking had
|
|
356
|
+
# to block the user.
|
|
357
|
+
def act(action, user_id)
|
|
358
|
+
if action == BAN
|
|
359
|
+
ban(user_id)
|
|
360
|
+
return BAN
|
|
361
|
+
end
|
|
362
|
+
revoke(user_id)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
private
|
|
366
|
+
|
|
367
|
+
def revoke(user_id)
|
|
368
|
+
user = Segment.encode(user_id)
|
|
369
|
+
ended = delete_sessions(user)
|
|
370
|
+
# Without the sessions API a login session survives; blocking is the only way to end it.
|
|
371
|
+
block(user) unless ended
|
|
372
|
+
delete_refresh_tokens(user_id, user)
|
|
373
|
+
ended ? REVOKE_SESSIONS : BAN
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Auth0 answers 403 feature_not_enabled when the tenant's plan does not include an endpoint.
|
|
377
|
+
def not_on_plan?(error)
|
|
378
|
+
error.status == 403 && error.code == 'feature_not_enabled'
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def block(user)
|
|
382
|
+
@call.call('PATCH', "/users/#{user}", body: { 'blocked' => true }, allow_404: true)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Ends every session; false when the plan has no sessions API.
|
|
386
|
+
def delete_sessions(user)
|
|
387
|
+
@call.call('DELETE', "/users/#{user}/sessions", allow_404: true)
|
|
388
|
+
true
|
|
389
|
+
rescue ProviderActionError => e
|
|
390
|
+
raise unless not_on_plan?(e)
|
|
391
|
+
|
|
392
|
+
false
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def delete_refresh_tokens(user_id, user)
|
|
396
|
+
begin
|
|
397
|
+
@call.call('DELETE', "/users/#{user}/refresh-tokens", allow_404: true)
|
|
398
|
+
return
|
|
399
|
+
rescue ProviderActionError => e
|
|
400
|
+
raise unless not_on_plan?(e)
|
|
401
|
+
end
|
|
402
|
+
# Collect every id before deleting any, so the pages do not shift underneath.
|
|
403
|
+
ids = []
|
|
404
|
+
%w[refresh_token rotating_refresh_token].each do |type|
|
|
405
|
+
10.times do |page|
|
|
406
|
+
body = @call.call('GET', '/device-credentials',
|
|
407
|
+
query: [['user_id', user_id], ['type', type], ['page', page.to_s], ['per_page', PAGE.to_s]],
|
|
408
|
+
allow_404: true)
|
|
409
|
+
listed = body.is_a?(Array) ? body.grep(Hash) : []
|
|
410
|
+
ids.concat(listed.map { |c| c['id'] }.select { |id| id.is_a?(String) && !id.empty? })
|
|
411
|
+
break if listed.length < PAGE
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
ids.each { |id| @call.call('DELETE', "/device-credentials/#{ERB::Util.url_encode(id)}", allow_404: true) }
|
|
315
415
|
end
|
|
316
416
|
end
|
|
317
417
|
|
|
@@ -95,9 +95,9 @@ module NoResponse
|
|
|
95
95
|
|
|
96
96
|
@language = "ruby"
|
|
97
97
|
@openapi_doc_version = "1"
|
|
98
|
-
@sdk_version = "2.1.
|
|
98
|
+
@sdk_version = "2.1.14"
|
|
99
99
|
@gen_version = "internal"
|
|
100
|
-
@user_agent = "noresponse-sdk/ruby 2.1.
|
|
100
|
+
@user_agent = "noresponse-sdk/ruby 2.1.14 internal 1 noresponse"
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
sig { returns([String, T::Hash[Symbol, String]]) }
|