noresponse 5.1.1 → 6.0.1
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/accounts.rb +663 -71
- data/lib/no_response/late_results.rb +10 -8
- data/lib/no_response/models/operations/listaccounts_request.rb +52 -0
- data/lib/no_response/models/operations/listaccounts_request.rbi +26 -0
- data/lib/no_response/models/operations/listaccounts_response.rb +32 -0
- data/lib/no_response/models/operations/listaccounts_response.rbi +20 -0
- data/lib/no_response/models/operations/recordenforcement_request.rb +33 -0
- data/lib/no_response/models/operations/recordenforcement_request.rbi +20 -0
- data/lib/no_response/models/operations/recordenforcement_response.rb +32 -0
- data/lib/no_response/models/operations/recordenforcement_response.rbi +20 -0
- data/lib/no_response/models/operations.rb +4 -0
- data/lib/no_response/models/shared/accountdeleted.rb +49 -0
- data/lib/no_response/models/shared/accountdeleted.rbi +26 -0
- data/lib/no_response/models/shared/accountdeletedevent.rb +70 -0
- data/lib/no_response/models/shared/accountdeletedevent.rbi +38 -0
- data/lib/no_response/models/shared/accountlist.rb +40 -0
- data/lib/no_response/models/shared/accountlist.rbi +20 -0
- data/lib/no_response/models/shared/accountriskchanged.rb +44 -0
- data/lib/no_response/models/shared/accountriskchanged.rbi +20 -0
- data/lib/no_response/models/shared/accountriskchangedevent.rb +70 -0
- data/lib/no_response/models/shared/accountriskchangedevent.rbi +38 -0
- data/lib/no_response/models/shared/assessmentexpired.rb +82 -0
- data/lib/no_response/models/shared/assessmentexpired.rbi +38 -0
- data/lib/no_response/models/shared/assessmentupdateddata.rb +23 -0
- data/lib/no_response/models/shared/assessmentupdateddata.rbi +9 -0
- data/lib/no_response/models/shared/assessmentupdatedevent.rb +79 -0
- data/lib/no_response/models/shared/assessmentupdatedevent.rbi +38 -0
- data/lib/no_response/models/shared/conflicterrorcode.rb +1 -0
- data/lib/no_response/models/shared/endpointverification.rb +41 -0
- data/lib/no_response/models/shared/endpointverification.rbi +20 -0
- data/lib/no_response/models/shared/endpointverificationevent.rb +70 -0
- data/lib/no_response/models/shared/endpointverificationevent.rbi +38 -0
- data/lib/no_response/models/shared/enforcement.rb +156 -0
- data/lib/no_response/models/shared/enforcement.rbi +68 -0
- data/lib/no_response/models/shared/enforcementaction.rb +23 -0
- data/lib/no_response/models/shared/enforcementaction.rbi +9 -0
- data/lib/no_response/models/shared/enforcementprovider.rb +21 -0
- data/lib/no_response/models/shared/enforcementprovider.rbi +9 -0
- data/lib/no_response/models/shared/enforcementrequest.rb +122 -0
- data/lib/no_response/models/shared/enforcementrequest.rbi +50 -0
- data/lib/no_response/models/shared/enforcementstate.rb +22 -0
- data/lib/no_response/models/shared/enforcementstate.rbi +9 -0
- data/lib/no_response/models/shared/enforcementstatus.rb +19 -0
- data/lib/no_response/models/shared/enforcementstatus.rbi +9 -0
- data/lib/no_response/models/shared/environment.rb +16 -0
- data/lib/no_response/models/shared/environment.rbi +9 -0
- data/lib/no_response/models/shared/webhookendpointdisabled.rb +49 -0
- data/lib/no_response/models/shared/webhookendpointdisabled.rbi +26 -0
- data/lib/no_response/models/shared/webhookendpointdisabledevent.rb +70 -0
- data/lib/no_response/models/shared/webhookendpointdisabledevent.rbi +38 -0
- data/lib/no_response/models/shared/webhookevent.rb +23 -0
- data/lib/no_response/models/shared/webhookevent.rbi +9 -0
- data/lib/no_response/models/shared/webhookverificationresponse.rb +33 -0
- data/lib/no_response/models/shared/webhookverificationresponse.rbi +14 -0
- data/lib/no_response/models/shared/workspacedeleted.rb +55 -0
- data/lib/no_response/models/shared/workspacedeleted.rbi +26 -0
- data/lib/no_response/models/shared/workspacedeletedevent.rb +70 -0
- data/lib/no_response/models/shared/workspacedeletedevent.rbi +38 -0
- data/lib/no_response/models/shared.rb +23 -0
- data/lib/no_response/sdk_hooks/registration.rb +0 -1
- data/lib/no_response/sdkconfiguration.rb +2 -2
- data/lib/no_response/webhook_verification.rb +170 -0
- data/lib/noresponse.rb +2 -0
- metadata +56 -2
- data/lib/no_response/webhooks.rb +0 -264
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: noresponse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- NoResponse
|
|
@@ -313,6 +313,10 @@ files:
|
|
|
313
313
|
- lib/no_response/models/operations/listaccountassessments_request.rbi
|
|
314
314
|
- lib/no_response/models/operations/listaccountassessments_response.rb
|
|
315
315
|
- lib/no_response/models/operations/listaccountassessments_response.rbi
|
|
316
|
+
- lib/no_response/models/operations/listaccounts_request.rb
|
|
317
|
+
- lib/no_response/models/operations/listaccounts_request.rbi
|
|
318
|
+
- lib/no_response/models/operations/listaccounts_response.rb
|
|
319
|
+
- lib/no_response/models/operations/listaccounts_response.rbi
|
|
316
320
|
- lib/no_response/models/operations/listintegrations_response.rb
|
|
317
321
|
- lib/no_response/models/operations/listintegrations_response.rbi
|
|
318
322
|
- lib/no_response/models/operations/listwebhookdeliveries_request.rb
|
|
@@ -327,6 +331,10 @@ files:
|
|
|
327
331
|
- lib/no_response/models/operations/recordassessmentoutcome_request.rbi
|
|
328
332
|
- lib/no_response/models/operations/recordassessmentoutcome_response.rb
|
|
329
333
|
- lib/no_response/models/operations/recordassessmentoutcome_response.rbi
|
|
334
|
+
- lib/no_response/models/operations/recordenforcement_request.rb
|
|
335
|
+
- lib/no_response/models/operations/recordenforcement_request.rbi
|
|
336
|
+
- lib/no_response/models/operations/recordenforcement_response.rb
|
|
337
|
+
- lib/no_response/models/operations/recordenforcement_response.rbi
|
|
330
338
|
- lib/no_response/models/operations/recordkeyusage_request.rb
|
|
331
339
|
- lib/no_response/models/operations/recordkeyusage_request.rbi
|
|
332
340
|
- lib/no_response/models/operations/recordkeyusage_response.rb
|
|
@@ -356,10 +364,20 @@ files:
|
|
|
356
364
|
- lib/no_response/models/shared/accountdecision.rbi
|
|
357
365
|
- lib/no_response/models/shared/accountdecisionsbyflow.rb
|
|
358
366
|
- lib/no_response/models/shared/accountdecisionsbyflow.rbi
|
|
367
|
+
- lib/no_response/models/shared/accountdeleted.rb
|
|
368
|
+
- lib/no_response/models/shared/accountdeleted.rbi
|
|
369
|
+
- lib/no_response/models/shared/accountdeletedevent.rb
|
|
370
|
+
- lib/no_response/models/shared/accountdeletedevent.rbi
|
|
359
371
|
- lib/no_response/models/shared/accountdeletion.rb
|
|
360
372
|
- lib/no_response/models/shared/accountdeletion.rbi
|
|
373
|
+
- lib/no_response/models/shared/accountlist.rb
|
|
374
|
+
- lib/no_response/models/shared/accountlist.rbi
|
|
361
375
|
- lib/no_response/models/shared/accountrisk.rb
|
|
362
376
|
- lib/no_response/models/shared/accountrisk.rbi
|
|
377
|
+
- lib/no_response/models/shared/accountriskchanged.rb
|
|
378
|
+
- lib/no_response/models/shared/accountriskchanged.rbi
|
|
379
|
+
- lib/no_response/models/shared/accountriskchangedevent.rb
|
|
380
|
+
- lib/no_response/models/shared/accountriskchangedevent.rbi
|
|
363
381
|
- lib/no_response/models/shared/accountriskreason.rb
|
|
364
382
|
- lib/no_response/models/shared/accountriskreason.rbi
|
|
365
383
|
- lib/no_response/models/shared/alternate_name.rb
|
|
@@ -378,6 +396,8 @@ files:
|
|
|
378
396
|
- lib/no_response/models/shared/assessmentdetailauthcontext.rbi
|
|
379
397
|
- lib/no_response/models/shared/assessmentdetailtiming.rb
|
|
380
398
|
- lib/no_response/models/shared/assessmentdetailtiming.rbi
|
|
399
|
+
- lib/no_response/models/shared/assessmentexpired.rb
|
|
400
|
+
- lib/no_response/models/shared/assessmentexpired.rbi
|
|
381
401
|
- lib/no_response/models/shared/assessmentlist.rb
|
|
382
402
|
- lib/no_response/models/shared/assessmentlist.rbi
|
|
383
403
|
- lib/no_response/models/shared/assessmentoutcome.rb
|
|
@@ -396,6 +416,10 @@ files:
|
|
|
396
416
|
- lib/no_response/models/shared/assessmentrequestissuedcredential.rbi
|
|
397
417
|
- lib/no_response/models/shared/assessmentrequestpolicycontext.rb
|
|
398
418
|
- lib/no_response/models/shared/assessmentrequestpolicycontext.rbi
|
|
419
|
+
- lib/no_response/models/shared/assessmentupdateddata.rb
|
|
420
|
+
- lib/no_response/models/shared/assessmentupdateddata.rbi
|
|
421
|
+
- lib/no_response/models/shared/assessmentupdatedevent.rb
|
|
422
|
+
- lib/no_response/models/shared/assessmentupdatedevent.rbi
|
|
399
423
|
- lib/no_response/models/shared/assurancelevel.rb
|
|
400
424
|
- lib/no_response/models/shared/assurancelevel.rbi
|
|
401
425
|
- lib/no_response/models/shared/authcontext.rb
|
|
@@ -434,6 +458,24 @@ files:
|
|
|
434
458
|
- lib/no_response/models/shared/email.rbi
|
|
435
459
|
- lib/no_response/models/shared/email_union.rb
|
|
436
460
|
- lib/no_response/models/shared/email_union.rbi
|
|
461
|
+
- lib/no_response/models/shared/endpointverification.rb
|
|
462
|
+
- lib/no_response/models/shared/endpointverification.rbi
|
|
463
|
+
- lib/no_response/models/shared/endpointverificationevent.rb
|
|
464
|
+
- lib/no_response/models/shared/endpointverificationevent.rbi
|
|
465
|
+
- lib/no_response/models/shared/enforcement.rb
|
|
466
|
+
- lib/no_response/models/shared/enforcement.rbi
|
|
467
|
+
- lib/no_response/models/shared/enforcementaction.rb
|
|
468
|
+
- lib/no_response/models/shared/enforcementaction.rbi
|
|
469
|
+
- lib/no_response/models/shared/enforcementprovider.rb
|
|
470
|
+
- lib/no_response/models/shared/enforcementprovider.rbi
|
|
471
|
+
- lib/no_response/models/shared/enforcementrequest.rb
|
|
472
|
+
- lib/no_response/models/shared/enforcementrequest.rbi
|
|
473
|
+
- lib/no_response/models/shared/enforcementstate.rb
|
|
474
|
+
- lib/no_response/models/shared/enforcementstate.rbi
|
|
475
|
+
- lib/no_response/models/shared/enforcementstatus.rb
|
|
476
|
+
- lib/no_response/models/shared/enforcementstatus.rbi
|
|
477
|
+
- lib/no_response/models/shared/environment.rb
|
|
478
|
+
- lib/no_response/models/shared/environment.rbi
|
|
437
479
|
- lib/no_response/models/shared/failure.rb
|
|
438
480
|
- lib/no_response/models/shared/failure.rbi
|
|
439
481
|
- lib/no_response/models/shared/failurereason.rb
|
|
@@ -568,12 +610,18 @@ files:
|
|
|
568
610
|
- lib/no_response/models/shared/webhookendpoint.rbi
|
|
569
611
|
- lib/no_response/models/shared/webhookendpointcreated.rb
|
|
570
612
|
- lib/no_response/models/shared/webhookendpointcreated.rbi
|
|
613
|
+
- lib/no_response/models/shared/webhookendpointdisabled.rb
|
|
614
|
+
- lib/no_response/models/shared/webhookendpointdisabled.rbi
|
|
615
|
+
- lib/no_response/models/shared/webhookendpointdisabledevent.rb
|
|
616
|
+
- lib/no_response/models/shared/webhookendpointdisabledevent.rbi
|
|
571
617
|
- lib/no_response/models/shared/webhookendpointlist.rb
|
|
572
618
|
- lib/no_response/models/shared/webhookendpointlist.rbi
|
|
573
619
|
- lib/no_response/models/shared/webhookendpointrequest.rb
|
|
574
620
|
- lib/no_response/models/shared/webhookendpointrequest.rbi
|
|
575
621
|
- lib/no_response/models/shared/webhookendpointstate.rb
|
|
576
622
|
- lib/no_response/models/shared/webhookendpointstate.rbi
|
|
623
|
+
- lib/no_response/models/shared/webhookevent.rb
|
|
624
|
+
- lib/no_response/models/shared/webhookevent.rbi
|
|
577
625
|
- lib/no_response/models/shared/webhookkeyrotation.rb
|
|
578
626
|
- lib/no_response/models/shared/webhookkeyrotation.rbi
|
|
579
627
|
- lib/no_response/models/shared/webhookkeyrotationrequest.rb
|
|
@@ -588,10 +636,16 @@ files:
|
|
|
588
636
|
- lib/no_response/models/shared/webhooktestrequest.rbi
|
|
589
637
|
- lib/no_response/models/shared/webhooktestrequestevent.rb
|
|
590
638
|
- lib/no_response/models/shared/webhooktestrequestevent.rbi
|
|
639
|
+
- lib/no_response/models/shared/webhookverificationresponse.rb
|
|
640
|
+
- lib/no_response/models/shared/webhookverificationresponse.rbi
|
|
591
641
|
- lib/no_response/models/shared/website.rb
|
|
592
642
|
- lib/no_response/models/shared/website.rbi
|
|
593
643
|
- lib/no_response/models/shared/website_union.rb
|
|
594
644
|
- lib/no_response/models/shared/website_union.rbi
|
|
645
|
+
- lib/no_response/models/shared/workspacedeleted.rb
|
|
646
|
+
- lib/no_response/models/shared/workspacedeleted.rbi
|
|
647
|
+
- lib/no_response/models/shared/workspacedeletedevent.rb
|
|
648
|
+
- lib/no_response/models/shared/workspacedeletedevent.rbi
|
|
595
649
|
- lib/no_response/noresponseclient.rb
|
|
596
650
|
- lib/no_response/sdk_hooks/hooks.rb
|
|
597
651
|
- lib/no_response/sdk_hooks/idempotency_key_hook.rb
|
|
@@ -608,8 +662,8 @@ files:
|
|
|
608
662
|
- lib/no_response/utils/url.rb
|
|
609
663
|
- lib/no_response/utils/utils.rb
|
|
610
664
|
- lib/no_response/wait.rb
|
|
665
|
+
- lib/no_response/webhook_verification.rb
|
|
611
666
|
- lib/no_response/webhookendpoints.rb
|
|
612
|
-
- lib/no_response/webhooks.rb
|
|
613
667
|
- lib/noresponse.rb
|
|
614
668
|
homepage:
|
|
615
669
|
licenses:
|
data/lib/no_response/webhooks.rb
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
# typed: true
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Webhook verification. Hand-written beside the generated client.
|
|
5
|
-
#
|
|
6
|
-
# Verify the exact bytes received: the signature is HMAC-SHA256 over
|
|
7
|
-
# "<timestamp>.<raw body>", so re-serialising parsed JSON breaks it.
|
|
8
|
-
|
|
9
|
-
require 'json'
|
|
10
|
-
require 'openssl'
|
|
11
|
-
require 'time'
|
|
12
|
-
|
|
13
|
-
module NoResponse
|
|
14
|
-
module Webhooks
|
|
15
|
-
DEFAULT_TOLERANCE_SECONDS = 300
|
|
16
|
-
|
|
17
|
-
REQUIRED = %w[nr-webhook-id nr-webhook-timestamp nr-webhook-key-id nr-webhook-signature].freeze
|
|
18
|
-
KEY_ID = /\Awhk_[A-Za-z0-9]{8,64}\z/
|
|
19
|
-
SIGNATURE = /\Av1=[0-9a-f]{64}\z/
|
|
20
|
-
TIMESTAMP = /\A[0-9]{1,12}\z/
|
|
21
|
-
CHALLENGE = /\A[A-Za-z0-9_-]{43}\z/
|
|
22
|
-
EVENT_ID = /\Aevt_[0-9A-HJKMNP-TV-Z]{26}\z/
|
|
23
|
-
ASSESSMENT_ID = /\Aasm_[0-9A-HJKMNP-TV-Z]{26}\z/
|
|
24
|
-
ACCOUNT_ID = /\A[A-Za-z0-9][A-Za-z0-9._:|-]{0,127}\z/
|
|
25
|
-
ENDPOINT_ID = /\Awhe_[0-9A-HJKMNP-TV-Z]{26}\z/
|
|
26
|
-
WORKSPACE_ID = /\A[A-Za-z0-9][A-Za-z0-9_.-]{0,122}\z/
|
|
27
|
-
|
|
28
|
-
# Raised by construct_event. Answer 400 and do not process the delivery.
|
|
29
|
-
# code is one of :missing_header, :duplicate_header, :invalid_timestamp,
|
|
30
|
-
# :unknown_key, :invalid_signature or :invalid_payload.
|
|
31
|
-
class VerificationError < StandardError
|
|
32
|
-
attr_reader :code
|
|
33
|
-
|
|
34
|
-
def initialize(code, message)
|
|
35
|
-
super(message)
|
|
36
|
-
@code = code
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# The final event for an assessment: it has expired and get now answers
|
|
41
|
-
# 404. assessment_version is the last served version.
|
|
42
|
-
AssessmentExpired = Struct.new(:assessment_id, :account_id, :assessment_version, keyword_init: true) do
|
|
43
|
-
def status = 'expired'
|
|
44
|
-
def change_reason = 'expired'
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
EndpointVerification = Struct.new(:endpoint_id, :challenge, keyword_init: true)
|
|
48
|
-
|
|
49
|
-
# An account you erased with accounts.delete: delete what you hold for it.
|
|
50
|
-
# assessments_deleted is at least 1, and deleted_at equals deleted_at in
|
|
51
|
-
# the delete answer.
|
|
52
|
-
AccountDeleted = Struct.new(:account_id, :assessments_deleted, :deleted_at, keyword_init: true)
|
|
53
|
-
|
|
54
|
-
# The workspace was deleted. Sent once per environment ("live" or "test")
|
|
55
|
-
# before its endpoints are torn down: the last event this endpoint receives.
|
|
56
|
-
WorkspaceDeleted = Struct.new(:workspace_id, :environment, :deleted_at, keyword_init: true)
|
|
57
|
-
|
|
58
|
-
# Another endpoint of this environment was disabled after failing
|
|
59
|
-
# continuously for 72 hours, since failing_since. It receives nothing more
|
|
60
|
-
# until it is verified again.
|
|
61
|
-
EndpointDisabled = Struct.new(:endpoint_id, :failing_since, :disabled_at, keyword_init: true)
|
|
62
|
-
|
|
63
|
-
# The account's decision for at least one flow changed. account is a
|
|
64
|
-
# Models::Shared::Account exactly as accounts.get returns it, with its new
|
|
65
|
-
# risk and decisions; previous_decisions_by_flow is a
|
|
66
|
-
# Models::Shared::AccountDecisionsByFlow holding the decisions it replaced.
|
|
67
|
-
# It is not sent when the score moves within the same decision, nor for an
|
|
68
|
-
# account's first risk.
|
|
69
|
-
AccountRiskChanged = Struct.new(:account, :previous_decisions_by_flow, keyword_init: true)
|
|
70
|
-
|
|
71
|
-
# A verified delivery. data is a Models::Shared::Assessment (exactly as
|
|
72
|
-
# assessments.get returns it) or an AssessmentExpired for type
|
|
73
|
-
# "assessment.updated", an EndpointVerification for
|
|
74
|
-
# "endpoint.verification", an AccountDeleted for "account.deleted", a
|
|
75
|
-
# WorkspaceDeleted for "workspace.deleted", an EndpointDisabled for
|
|
76
|
-
# "webhook_endpoint.disabled" and an AccountRiskChanged for
|
|
77
|
-
# "account.risk_changed". Deliveries are at least once and unordered:
|
|
78
|
-
# deduplicate on id and keep the highest detail.assessment_version.
|
|
79
|
-
Event = Struct.new(:id, :type, :created_at, :data, :test, keyword_init: true) do
|
|
80
|
-
# True for the terminal expiry event: delete what you hold and fetch nothing.
|
|
81
|
-
def expired? = data.is_a?(AssessmentExpired)
|
|
82
|
-
|
|
83
|
-
# True on an event sent by the send-test-event call: data is a fixed
|
|
84
|
-
# example assessment, not one of yours. Acknowledge it and apply nothing.
|
|
85
|
-
def test? = test == true
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
class << self
|
|
89
|
-
|
|
90
|
-
# Verify one delivery and return the typed Event.
|
|
91
|
-
#
|
|
92
|
-
# payload is the raw request body. headers is a Hash, Rack env-style
|
|
93
|
-
# pairs or any object with each; each NR-Webhook-* header must appear
|
|
94
|
-
# exactly once, in any letter case. secrets maps each whk_ key id
|
|
95
|
-
# NoResponse gave you to its secret; hold both during a rotation.
|
|
96
|
-
def construct_event(payload, headers, secrets, tolerance_seconds: DEFAULT_TOLERANCE_SECONDS, now: Time.now.to_i)
|
|
97
|
-
raw = payload.to_s.b
|
|
98
|
-
found = collect(headers)
|
|
99
|
-
timestamp = found['nr-webhook-timestamp']
|
|
100
|
-
if !TIMESTAMP.match?(timestamp) || (now.to_i - timestamp.to_i).abs > tolerance_seconds
|
|
101
|
-
raise VerificationError.new(:invalid_timestamp, 'Webhook timestamp is outside the replay window')
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
key_id = found['nr-webhook-key-id']
|
|
105
|
-
unless KEY_ID.match?(key_id) && secrets.key?(key_id)
|
|
106
|
-
raise VerificationError.new(:unknown_key, 'Webhook key id is not one this receiver holds')
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
signature = found['nr-webhook-signature']
|
|
110
|
-
raise VerificationError.new(:invalid_signature, 'Webhook signature is malformed') unless SIGNATURE.match?(signature)
|
|
111
|
-
|
|
112
|
-
expected = OpenSSL::HMAC.hexdigest('SHA256', secrets.fetch(key_id).to_s, "#{timestamp}.".b + raw)
|
|
113
|
-
unless OpenSSL.fixed_length_secure_compare(expected, signature.delete_prefix('v1='))
|
|
114
|
-
raise VerificationError.new(:invalid_signature, 'Webhook signature does not match')
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
event(raw, found['nr-webhook-id'])
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# The body to answer an endpoint.verification event with, as JSON, status 200.
|
|
121
|
-
def verification_response(event)
|
|
122
|
-
unless event.data.is_a?(EndpointVerification)
|
|
123
|
-
raise VerificationError.new(:invalid_payload, 'Not an endpoint.verification event')
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
{ 'challenge' => event.data.challenge }
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
private
|
|
130
|
-
|
|
131
|
-
def collect(headers)
|
|
132
|
-
found = {}
|
|
133
|
-
headers.each do |name, value|
|
|
134
|
-
key = name.to_s.downcase.tr('_', '-').delete_prefix('http-')
|
|
135
|
-
next unless REQUIRED.include?(key)
|
|
136
|
-
|
|
137
|
-
values = Array(value)
|
|
138
|
-
if values.length != 1 || found.key?(key)
|
|
139
|
-
raise VerificationError.new(:duplicate_header, "Webhook header #{key} is repeated")
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
found[key] = values.first.to_s
|
|
143
|
-
end
|
|
144
|
-
REQUIRED.each do |key|
|
|
145
|
-
raise VerificationError.new(:missing_header, "Webhook header #{key} is missing") if found[key].to_s.empty?
|
|
146
|
-
end
|
|
147
|
-
found
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def event(raw, header_id)
|
|
151
|
-
body = begin
|
|
152
|
-
JSON.parse(raw)
|
|
153
|
-
rescue JSON::ParserError
|
|
154
|
-
raise VerificationError.new(:invalid_payload, 'Webhook body is not JSON')
|
|
155
|
-
end
|
|
156
|
-
unless body.is_a?(Hash) && body['id'] == header_id && EVENT_ID.match?(body['id'].to_s)
|
|
157
|
-
raise VerificationError.new(:invalid_payload, 'Webhook event id does not match NR-Webhook-Id')
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
data = body['data']
|
|
161
|
-
unless body['created_at'].is_a?(String) && valid_datetime?(body['created_at']) && data.is_a?(Hash)
|
|
162
|
-
raise VerificationError.new(:invalid_payload, 'Webhook event envelope is malformed')
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
Event.new(id: body['id'], type: body['type'], created_at: body['created_at'], data: event_data(body['type'], data),
|
|
166
|
-
test: body['test'] == true && body['type'] == 'assessment.updated')
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def event_data(type, data)
|
|
170
|
-
case type
|
|
171
|
-
when 'endpoint.verification'
|
|
172
|
-
unless ENDPOINT_ID.match?(data['endpoint_id'].to_s) && CHALLENGE.match?(data['challenge'].to_s)
|
|
173
|
-
raise VerificationError.new(:invalid_payload, 'Endpoint verification event is malformed')
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
EndpointVerification.new(endpoint_id: data['endpoint_id'], challenge: data['challenge'])
|
|
177
|
-
when 'assessment.updated'
|
|
178
|
-
return expired(data) if data['status'] == 'expired'
|
|
179
|
-
begin
|
|
180
|
-
Crystalline.unmarshal_json(data, Models::Shared::Assessment)
|
|
181
|
-
rescue KeyError, TypeError, ArgumentError, NoMethodError
|
|
182
|
-
raise VerificationError.new(:invalid_payload, 'Webhook data is not an assessment')
|
|
183
|
-
end
|
|
184
|
-
when 'account.deleted'
|
|
185
|
-
account_deleted(data)
|
|
186
|
-
when 'workspace.deleted'
|
|
187
|
-
workspace_deleted(data)
|
|
188
|
-
when 'webhook_endpoint.disabled'
|
|
189
|
-
endpoint_disabled(data)
|
|
190
|
-
when 'account.risk_changed'
|
|
191
|
-
account_risk_changed(data)
|
|
192
|
-
else
|
|
193
|
-
raise VerificationError.new(:invalid_payload, 'Unknown webhook event type')
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def expired(data)
|
|
198
|
-
unless ASSESSMENT_ID.match?(data['assessment_id'].to_s) &&
|
|
199
|
-
ACCOUNT_ID.match?(data['account_id'].to_s) && data['assessment_version'].is_a?(Integer) &&
|
|
200
|
-
data['assessment_version'] >= 1 && data['status'] == 'expired' && data['change_reason'] == 'expired'
|
|
201
|
-
raise VerificationError.new(:invalid_payload, 'Expired assessment event is malformed')
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
AssessmentExpired.new(assessment_id: data['assessment_id'], account_id: data['account_id'],
|
|
205
|
-
assessment_version: data['assessment_version'])
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def account_deleted(data)
|
|
209
|
-
unless data['account_id'].is_a?(String) && ACCOUNT_ID.match?(data['account_id']) &&
|
|
210
|
-
data['assessments_deleted'].is_a?(Integer) && data['assessments_deleted'] >= 1 &&
|
|
211
|
-
data['deleted_at'].is_a?(String) && valid_datetime?(data['deleted_at'])
|
|
212
|
-
raise VerificationError.new(:invalid_payload, 'Account deleted event is malformed')
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
AccountDeleted.new(account_id: data['account_id'], assessments_deleted: data['assessments_deleted'],
|
|
216
|
-
deleted_at: data['deleted_at'])
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
def workspace_deleted(data)
|
|
220
|
-
unless data['workspace_id'].is_a?(String) && WORKSPACE_ID.match?(data['workspace_id']) &&
|
|
221
|
-
%w[live test].include?(data['environment']) &&
|
|
222
|
-
data['deleted_at'].is_a?(String) && valid_datetime?(data['deleted_at'])
|
|
223
|
-
raise VerificationError.new(:invalid_payload, 'Workspace deleted event is malformed')
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
WorkspaceDeleted.new(workspace_id: data['workspace_id'], environment: data['environment'],
|
|
227
|
-
deleted_at: data['deleted_at'])
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def endpoint_disabled(data)
|
|
231
|
-
unless data['endpoint_id'].is_a?(String) && ENDPOINT_ID.match?(data['endpoint_id']) &&
|
|
232
|
-
data['failing_since'].is_a?(String) && valid_datetime?(data['failing_since']) &&
|
|
233
|
-
data['disabled_at'].is_a?(String) && valid_datetime?(data['disabled_at'])
|
|
234
|
-
raise VerificationError.new(:invalid_payload, 'Endpoint disabled event is malformed')
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
EndpointDisabled.new(endpoint_id: data['endpoint_id'], failing_since: data['failing_since'],
|
|
238
|
-
disabled_at: data['disabled_at'])
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def account_risk_changed(data)
|
|
242
|
-
account = data['account']
|
|
243
|
-
previous = data['previous_decisions_by_flow']
|
|
244
|
-
unless account.is_a?(Hash) && previous.is_a?(Hash) && account['id'].is_a?(String) && ACCOUNT_ID.match?(account['id'])
|
|
245
|
-
raise VerificationError.new(:invalid_payload, 'Account risk changed event is malformed')
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
begin
|
|
249
|
-
AccountRiskChanged.new(account: Crystalline.unmarshal_json(account, Models::Shared::Account),
|
|
250
|
-
previous_decisions_by_flow: Crystalline.unmarshal_json(previous, Models::Shared::AccountDecisionsByFlow))
|
|
251
|
-
rescue KeyError, TypeError, ArgumentError, NoMethodError
|
|
252
|
-
raise VerificationError.new(:invalid_payload, 'Account risk changed event is malformed')
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def valid_datetime?(value)
|
|
257
|
-
Time.iso8601(value)
|
|
258
|
-
true
|
|
259
|
-
rescue ArgumentError
|
|
260
|
-
false
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
end
|