noresponse 10.0.0 → 10.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e55989c19fddccb91ddc256165c976402eb3d9a5712b00bbb2b0a0a6f1f4afa
4
- data.tar.gz: 71626ebd2ea9eb53dda2073c04042a2c845e27c2b0bde7ace9334f59ff1f872b
3
+ metadata.gz: 47be67d4a5ea5ad8b80822a30612f68c07105ecf68e7c4d2c9589352612ba67a
4
+ data.tar.gz: 804eb7cbce3840df38b218c7644868aef1692bb0b4557794f725d45687fe097c
5
5
  SHA512:
6
- metadata.gz: cc890872c6ce60805f9f331962d4b47d2e81bb36974832b5168ef092b8a6aede034c6a3df16667dacdc66730a15a9d51e1bb9dd88ae27b87fbc0fb025798c6d6
7
- data.tar.gz: bc2dab6ef691946033ebca01af8fa3718a270b79ff611aca52fb93f7a23811da5fc742eab40c5d511d86e6b35e51613583094de4160acba2ea833a2c4fdb4582
6
+ metadata.gz: d6a91801af8bbc5d579de77fd601334c6c3e4ea00fe0c06f0be10080d6ada6ff9b74912351927ce924cb77d805fe515b5a0ac7609eb5a380d208128aa64ccb1a
7
+ data.tar.gz: 41acbf922cadaab5d58f52a99e5372dedd793f842a6eb56bd07a901074f19b5e8b7a0dbf325bffbcb842c0b463411e004471935883264810078a3b99072c50ae
@@ -50,7 +50,7 @@ module NoResponse
50
50
  }
51
51
  def create(body:, workspace_id:, idempotency_key: nil, timeout_ms: nil, http_headers: nil)
52
52
  # create - Request an asynchronous customer export.
53
- # Request an encrypted JSON Lines snapshot of retained account or audit records. Requires exports plus history or audit scope; detailed account data also needs evidence or detailed scope. Criteria are frozen and hashed. Each environment permits four active jobs, 64 MiB per artifact, and 256 KiB per chunk. Artifacts expire after 24 hours. Send an Idempotency-Key to safely repeat a request. Every operation checks current permissions and source retention.
53
+ # Request an encrypted JSON Lines snapshot of retained account or audit records. Requires exports plus history or audit scope. An account export without legacy history needs investigate with evidence or detailed, because it holds submitted identifiers; detailed account data always needs evidence or detailed. Criteria are frozen and hashed. Each environment permits four active jobs, 64 MiB per artifact, and 256 KiB per chunk. Artifacts expire after 24 hours. Send an Idempotency-Key to safely repeat a request. Every operation checks current permissions and source retention.
54
54
  request = Models::Operations::CreateCustomerExportRequest.new(
55
55
  workspace_id: workspace_id,
56
56
  body: body,
@@ -73,26 +73,81 @@ module NoResponse
73
73
  # current risk. A delivery that fails verification raises
74
74
  # WebhookVerification::VerificationError before anything is read; any
75
75
  # other event, and a test event, does nothing.
76
+ #
77
+ # with_account_lock serializes enforcement of one account across your
78
+ # workers. It is called with the account id and a callable: hold an
79
+ # exclusive lock on the account, call the callable, store the result's
80
+ # risk_version and any state change, then release the lock. An exception
81
+ # must propagate. Inside the lock the handler reads the account's risk and
82
+ # your state again, so a stale event or listed account cannot undo a newer
83
+ # decision. Without it, overlapping calls can act on an old decision after
84
+ # a newer one.
76
85
  def handle_risk_change(client, payload, headers, secrets, provider:, get_account_state:, flows: {},
77
- tolerance_seconds: WebhookVerification::DEFAULT_TOLERANCE_SECONDS, now: nil)
86
+ tolerance_seconds: WebhookVerification::DEFAULT_TOLERANCE_SECONDS, now: nil,
87
+ with_account_lock: nil)
78
88
  event = WebhookVerification.construct_event(payload, headers, secrets, tolerance_seconds: tolerance_seconds, now: now)
79
89
  if event.type != 'account.risk_changed' || (event.respond_to?(:test) && event.test == true)
80
90
  return EnforcementResult.new(event: event, skipped: 'not_actionable', enforcements: [])
81
91
  end
82
92
 
83
- begin
84
- account = client.accounts.get(account_id: event.data.account.id).result
85
- rescue Models::Errors::NotFoundError
86
- return EnforcementResult.new(event: event, skipped: 'account_gone', enforcements: [])
87
- end
88
- result = enforce_account(client, account, provider: provider, get_account_state: get_account_state, flows: flows)
93
+ result = under_account_lock(client, event.data.account.id, provider, get_account_state, flows, with_account_lock)
89
94
  result.event = event
90
95
  result
91
96
  end
92
97
 
93
- # Enforce one account as read: the account a handler fetched, or one from
94
- # accounts.list_changed(unenforced: true) when catching up.
95
- def enforce_account(client, account, provider:, get_account_state:, flows: {})
98
+ # Enforce one account: the account a handler fetched, or one from
99
+ # accounts.list_changed(unenforced: true) when catching up. With
100
+ # with_account_lock the account is read again inside the lock; without it
101
+ # the account is enforced as given.
102
+ def enforce_account(client, account, provider:, get_account_state:, flows: {}, with_account_lock: nil)
103
+ return under_account_lock(client, account.id, provider, get_account_state, flows, with_account_lock) if with_account_lock
104
+
105
+ enforce_as_read(client, account, provider, get_account_state, flows)
106
+ end
107
+
108
+ # Clerk, WorkOS or Auth0, run from the shared action table. token is the
109
+ # Clerk secret key, WorkOS API key or Auth0 Management API token; domain
110
+ # is the Auth0 tenant domain. Pass http (a Faraday connection) for a proxy or for tests.
111
+ def identity_provider(name, token:, domain: nil, base_url: nil, http: nil)
112
+ name = name.to_s
113
+ spec = TABLE.fetch('providers')[name] or raise ArgumentError, "unknown identity provider #{name.inspect}"
114
+ raise ArgumentError, "#{name} needs token" if token.to_s.empty?
115
+
116
+ base = base_url || spec.fetch('base_url')
117
+ if base.include?('{domain}')
118
+ raise ArgumentError, "#{name} needs domain or base_url" if domain.to_s.empty?
119
+
120
+ base = base.sub('{domain}', domain)
121
+ end
122
+ TableProvider.new(name, spec, base.sub(%r{/+\z}, ''), token, http || Faraday.new(request: { timeout: 10 }))
123
+ end
124
+
125
+ # Your own user store: one callable per action you support, keyed by
126
+ # action. A callable may return other actions it ran. An action without one fails.
127
+ def custom_provider(handlers)
128
+ CustomProvider.new(handlers.transform_keys(&:to_s))
129
+ end
130
+
131
+ private
132
+
133
+ # Read the account's current risk and enforce it, inside with_account_lock when you gave one.
134
+ def under_account_lock(client, account_id, provider, get_account_state, flows, with_account_lock)
135
+ enforce = lambda do
136
+ begin
137
+ account = client.accounts.get(account_id: account_id).result
138
+ rescue Models::Errors::NotFoundError
139
+ next EnforcementResult.new(skipped: 'account_gone', enforcements: [])
140
+ end
141
+ enforce_as_read(client, account, provider, get_account_state, flows)
142
+ end
143
+ return enforce.call unless with_account_lock
144
+
145
+ result = nil
146
+ with_account_lock.call(account_id, -> { result = enforce.call })
147
+ result or raise 'with_account_lock returned without running enforce'
148
+ end
149
+
150
+ def enforce_as_read(client, account, provider, get_account_state, flows)
96
151
  answer = get_account_state.call(account)
97
152
  return EnforcementResult.new(account: account, skipped: 'unknown_user', enforcements: []) if answer.nil?
98
153
 
@@ -149,31 +204,6 @@ module NoResponse
149
204
  EnforcementResult.new(account: account, state: state, risk_version: risk_version, enforcements: enforcements)
150
205
  end
151
206
 
152
- # Clerk, WorkOS or Auth0, run from the shared action table. token is the
153
- # Clerk secret key, WorkOS API key or Auth0 Management API token; domain
154
- # is the Auth0 tenant domain. Pass http (a Faraday connection) for a proxy or for tests.
155
- def identity_provider(name, token:, domain: nil, base_url: nil, http: nil)
156
- name = name.to_s
157
- spec = TABLE.fetch('providers')[name] or raise ArgumentError, "unknown identity provider #{name.inspect}"
158
- raise ArgumentError, "#{name} needs token" if token.to_s.empty?
159
-
160
- base = base_url || spec.fetch('base_url')
161
- if base.include?('{domain}')
162
- raise ArgumentError, "#{name} needs domain or base_url" if domain.to_s.empty?
163
-
164
- base = base.sub('{domain}', domain)
165
- end
166
- TableProvider.new(name, spec, base.sub(%r{/+\z}, ''), token, http || Faraday.new(request: { timeout: 10 }))
167
- end
168
-
169
- # Your own user store: one callable per action you support, keyed by
170
- # action. A callable may return other actions it ran. An action without one fails.
171
- def custom_provider(handlers)
172
- CustomProvider.new(handlers.transform_keys(&:to_s))
173
- end
174
-
175
- private
176
-
177
207
  # The error_code of a failed action: the provider's code, else its HTTP status, as <provider>.<code>.
178
208
  def failure_code(provider, error)
179
209
  code = error.respond_to?(:code) ? error.code : nil
@@ -948,7 +948,7 @@ module NoResponse
948
948
  }
949
949
  def preview(body:, workspace_id:, environment:, retries: nil, timeout_ms: nil, http_headers: nil)
950
950
  # preview - Preview a list override change without side effects.
951
- # Preview canonical list matches and current override effects with settings or management. The result states its bounded coverage and changes no list entries.
951
+ # Preview canonical list matches and current override effects with settings or management, plus legacy history, or investigate with evidence or detailed. The result states its bounded coverage and changes no list entries.
952
952
  request = Models::Operations::PreviewListRequest.new(
953
953
  workspace_id: workspace_id,
954
954
  environment: environment,
@@ -0,0 +1,21 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module NoResponse
5
+ module Models
6
+ module Shared
7
+ # Attribute - name_gender is the records' gender against the submitted first name's usual one.
8
+ class Attribute
9
+ include ::Crystalline::Enum
10
+ enums do
11
+ BIRTH_DATE = new("birth_date")
12
+ GENDER = new("gender")
13
+ NATIONALITY = new("nationality")
14
+ NAME_GENDER = new("name_gender")
15
+ end
16
+
17
+ open!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ class NoResponse::Models::Shared::Attribute
5
+ extend ::Crystalline::MetadataFields::ClassMethods
6
+ end
7
+
8
+ class NoResponse::Models::Shared::Attribute
9
+ end
@@ -80,6 +80,18 @@ module NoResponse
80
80
  Crystalline::Nilable.new(::Integer),
81
81
  {'format_json': {'letter_case': ::NoResponse::Utils.field_name("block_threshold")}}
82
82
  )
83
+ # The score at which this answer's flow starts to recommend step_up. Absent from an answer stored before it was recorded.
84
+ field(
85
+ :step_up_threshold,
86
+ Crystalline::Nilable.new(::Integer),
87
+ {'format_json': {'letter_case': ::NoResponse::Utils.field_name("step_up_threshold")}}
88
+ )
89
+ # The score at which this answer's flow starts to recommend review. An answer nothing answered recommends review below it, with confidence unavailable. Absent from an answer stored before it was recorded.
90
+ field(
91
+ :review_threshold,
92
+ Crystalline::Nilable.new(::Integer),
93
+ {'format_json': {'letter_case': ::NoResponse::Utils.field_name("review_threshold")}}
94
+ )
83
95
  # Labels the v0 policy as hand-authored and not statistically validated.
84
96
  field(
85
97
  :policy_basis,
@@ -110,6 +122,8 @@ module NoResponse
110
122
  coverage: Models::Shared::RiskCoverage,
111
123
  block: T.nilable(T::Boolean),
112
124
  block_threshold: T.nilable(::Integer),
125
+ step_up_threshold: T.nilable(::Integer),
126
+ review_threshold: T.nilable(::Integer),
113
127
  policy_basis: ::String,
114
128
  summary: T.nilable(Models::Shared::RiskSummary),
115
129
  details: T.nilable(Models::Shared::RiskDetails)
@@ -126,6 +140,8 @@ module NoResponse
126
140
  coverage:,
127
141
  block: nil,
128
142
  block_threshold: nil,
143
+ step_up_threshold: nil,
144
+ review_threshold: nil,
129
145
  policy_basis: "heuristic_unvalidated",
130
146
  summary: nil,
131
147
  details: nil
@@ -139,6 +155,8 @@ module NoResponse
139
155
  @coverage = coverage
140
156
  @block = block
141
157
  @block_threshold = block_threshold
158
+ @step_up_threshold = step_up_threshold
159
+ @review_threshold = review_threshold
142
160
  unless policy_basis == "heuristic_unvalidated"
143
161
  raise ArgumentError, "Invalid value for policy_basis"
144
162
  end
@@ -160,6 +178,8 @@ module NoResponse
160
178
  return false unless @coverage == other.coverage
161
179
  return false unless @block == other.block
162
180
  return false unless @block_threshold == other.block_threshold
181
+ return false unless @step_up_threshold == other.step_up_threshold
182
+ return false unless @review_threshold == other.review_threshold
163
183
  return false unless @policy_basis == other.policy_basis
164
184
  return false unless @summary == other.summary
165
185
  return false unless @details == other.details
@@ -60,6 +60,18 @@ class NoResponse::Models::Shared::Risk
60
60
  def block_threshold=(str_)
61
61
  end
62
62
 
63
+ def step_up_threshold
64
+ end
65
+
66
+ def step_up_threshold=(str_)
67
+ end
68
+
69
+ def review_threshold
70
+ end
71
+
72
+ def review_threshold=(str_)
73
+ end
74
+
63
75
  def policy_basis
64
76
  end
65
77
 
@@ -38,6 +38,12 @@ module NoResponse
38
38
  Crystalline::Nilable.new(Crystalline::Array.new(Models::Shared::OnlineAccount)),
39
39
  {'format_json': {'letter_case': ::NoResponse::Utils.field_name("accounts")}}
40
40
  )
41
+ # What an identity_attribute_conflict reason weighed: each attribute the records disagree on and the sites reporting it. Absent when the reason did not fire, and on an answer stored before conflicts were recorded. No value is served.
42
+ field(
43
+ :attribute_conflicts,
44
+ Crystalline::Nilable.new(Crystalline::Array.new(Models::Shared::RiskDetailsAttributeConflict)),
45
+ {'format_json': {'letter_case': ::NoResponse::Utils.field_name("attribute_conflicts")}}
46
+ )
41
47
 
42
48
  sig {
43
49
  params(
@@ -45,16 +51,25 @@ module NoResponse
45
51
  breaches: T.nilable(T::Array[Models::Shared::Breach]),
46
52
  breach_names: T.nilable(T::Array[Models::Shared::RiskDetailsBreachName]),
47
53
  events: T.nilable(T::Array[Models::Shared::RiskDetailsEvent]),
48
- accounts: T.nilable(T::Array[Models::Shared::OnlineAccount])
54
+ accounts: T.nilable(T::Array[Models::Shared::OnlineAccount]),
55
+ attribute_conflicts: T.nilable(T::Array[Models::Shared::RiskDetailsAttributeConflict])
49
56
  )
50
57
  .void
51
58
  }
52
- def initialize(phone_names: nil, breaches: nil, breach_names: nil, events: nil, accounts: nil)
59
+ def initialize(
60
+ phone_names: nil,
61
+ breaches: nil,
62
+ breach_names: nil,
63
+ events: nil,
64
+ accounts: nil,
65
+ attribute_conflicts: nil
66
+ )
53
67
  @phone_names = phone_names
54
68
  @breaches = breaches
55
69
  @breach_names = breach_names
56
70
  @events = events
57
71
  @accounts = accounts
72
+ @attribute_conflicts = attribute_conflicts
58
73
  end
59
74
 
60
75
  sig { params(other: T.untyped).returns(T::Boolean) }
@@ -65,6 +80,7 @@ module NoResponse
65
80
  return false unless @breach_names == other.breach_names
66
81
  return false unless @events == other.events
67
82
  return false unless @accounts == other.accounts
83
+ return false unless @attribute_conflicts == other.attribute_conflicts
68
84
  true
69
85
  end
70
86
  end
@@ -35,4 +35,10 @@ class NoResponse::Models::Shared::RiskDetails
35
35
 
36
36
  def accounts=(str_)
37
37
  end
38
+
39
+ def attribute_conflicts
40
+ end
41
+
42
+ def attribute_conflicts=(str_)
43
+ end
38
44
  end
@@ -0,0 +1,62 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module NoResponse
5
+ module Models
6
+ module Shared
7
+
8
+ class RiskDetailsAttributeConflict
9
+ extend T::Sig
10
+ include Crystalline::MetadataFields
11
+
12
+ # name_gender is the records' gender against the submitted first name's usual one.
13
+ field(
14
+ :attribute,
15
+ Models::Shared::Attribute,
16
+ {
17
+ 'format_json': {
18
+ 'letter_case': ::NoResponse::Utils.field_name("attribute"),
19
+ required: true,
20
+ 'decoder': ::NoResponse::Utils.open_enum_from_string(Models::Shared::Attribute, false)
21
+ }
22
+ }
23
+ )
24
+ # The domains of the breached sites whose tables report the attribute, newest breach first, at most 32. Absent when none of the tables has a site.
25
+ field(
26
+ :sites,
27
+ Crystalline::Nilable.new(Crystalline::Array.new(::String)),
28
+ {'format_json': {'letter_case': ::NoResponse::Utils.field_name("sites")}}
29
+ )
30
+ # How many of the tables reporting the attribute have no site, such as a credential list or a stealer log. Absent when none.
31
+ field(
32
+ :unsited,
33
+ Crystalline::Nilable.new(::Integer),
34
+ {'format_json': {'letter_case': ::NoResponse::Utils.field_name("unsited")}}
35
+ )
36
+
37
+ sig {
38
+ params(
39
+ attribute: Models::Shared::Attribute,
40
+ sites: T.nilable(T::Array[::String]),
41
+ unsited: T.nilable(::Integer)
42
+ )
43
+ .void
44
+ }
45
+ def initialize(attribute:, sites: nil, unsited: nil)
46
+ @attribute = attribute
47
+ @sites = sites
48
+ @unsited = unsited
49
+ end
50
+
51
+ sig { params(other: T.untyped).returns(T::Boolean) }
52
+ def ==(other)
53
+ return false unless other.is_a?(self.class)
54
+ return false unless @attribute == other.attribute
55
+ return false unless @sites == other.sites
56
+ return false unless @unsited == other.unsited
57
+ true
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,26 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ class NoResponse::Models::Shared::RiskDetailsAttributeConflict
5
+ extend ::Crystalline::MetadataFields::ClassMethods
6
+ end
7
+
8
+ class NoResponse::Models::Shared::RiskDetailsAttributeConflict
9
+ def attribute
10
+ end
11
+
12
+ def attribute=(str_)
13
+ end
14
+
15
+ def sites
16
+ end
17
+
18
+ def sites=(str_)
19
+ end
20
+
21
+ def unsited
22
+ end
23
+
24
+ def unsited=(str_)
25
+ end
26
+ end
@@ -70,6 +70,7 @@ module NoResponse
70
70
  autoload :AssessmentUpdatedData, "no_response/models/shared/assessmentupdateddata.rb"
71
71
  autoload :AssessmentUpdatedEvent, "no_response/models/shared/assessmentupdatedevent.rb"
72
72
  autoload :AssuranceLevel, "no_response/models/shared/assurancelevel.rb"
73
+ autoload :Attribute, "no_response/models/shared/attribute.rb"
73
74
  autoload :AttributeAgreement, "no_response/models/shared/attributeagreement.rb"
74
75
  autoload :AuditEvent, "no_response/models/shared/auditevent.rb"
75
76
  autoload :AuditEventEvent, "no_response/models/shared/auditeventevent.rb"
@@ -310,6 +311,7 @@ module NoResponse
310
311
  autoload :RiskConfidence, "no_response/models/shared/riskconfidence.rb"
311
312
  autoload :RiskCoverage, "no_response/models/shared/riskcoverage.rb"
312
313
  autoload :RiskDetails, "no_response/models/shared/riskdetails.rb"
314
+ autoload :RiskDetailsAttributeConflict, "no_response/models/shared/riskdetailsattributeconflict.rb"
313
315
  autoload :RiskDetailsBreachName, "no_response/models/shared/riskdetailsbreachname.rb"
314
316
  autoload :RiskDetailsEvent, "no_response/models/shared/riskdetailsevent.rb"
315
317
  autoload :RiskDetailsEventKind, "no_response/models/shared/riskdetailseventkind.rb"
@@ -95,9 +95,9 @@ module NoResponse
95
95
 
96
96
  @language = "ruby"
97
97
  @openapi_doc_version = "1"
98
- @sdk_version = "10.0.0"
98
+ @sdk_version = "10.0.2"
99
99
  @gen_version = "internal"
100
- @user_agent = "noresponse-sdk/ruby 10.0.0 internal 1 noresponse"
100
+ @user_agent = "noresponse-sdk/ruby 10.0.2 internal 1 noresponse"
101
101
  end
102
102
 
103
103
  sig { returns([String, T::Hash[Symbol, String]]) }
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: 10.0.0
4
+ version: 10.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - NoResponse
@@ -807,6 +807,8 @@ files:
807
807
  - lib/no_response/models/shared/assessmentupdatedevent.rbi
808
808
  - lib/no_response/models/shared/assurancelevel.rb
809
809
  - lib/no_response/models/shared/assurancelevel.rbi
810
+ - lib/no_response/models/shared/attribute.rb
811
+ - lib/no_response/models/shared/attribute.rbi
810
812
  - lib/no_response/models/shared/attributeagreement.rb
811
813
  - lib/no_response/models/shared/attributeagreement.rbi
812
814
  - lib/no_response/models/shared/auditevent.rb
@@ -1295,6 +1297,8 @@ files:
1295
1297
  - lib/no_response/models/shared/riskcoverage.rbi
1296
1298
  - lib/no_response/models/shared/riskdetails.rb
1297
1299
  - lib/no_response/models/shared/riskdetails.rbi
1300
+ - lib/no_response/models/shared/riskdetailsattributeconflict.rb
1301
+ - lib/no_response/models/shared/riskdetailsattributeconflict.rbi
1298
1302
  - lib/no_response/models/shared/riskdetailsbreachname.rb
1299
1303
  - lib/no_response/models/shared/riskdetailsbreachname.rbi
1300
1304
  - lib/no_response/models/shared/riskdetailsevent.rb