smplkit 1.0.23 → 1.0.24

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: bff1572898b8af4d21af85f39f82e0e7b684173ef64b86163c81dacf20563028
4
- data.tar.gz: 50adc24985da3c81d82ed2847716e333c8d9589f74fc6eb86a7d53f6b4ae054e
3
+ metadata.gz: 716ac41c7f53fba00f896876246f36ca44e3db661aa3306d647675972edd64e9
4
+ data.tar.gz: 96d4b2c60234daea039444a9e05ee9441adcf84bc99cd21b690fa504ab1b5ff8
5
5
  SHA512:
6
- metadata.gz: bcb2099f897e6a2590abdf0bf56067bed0b0e876620a93d34fb074d10c30d81e081923024301f5a10d8a85230fdf8081f642957c3e368bb0b41307e15f1fe0f7
7
- data.tar.gz: 4e8ebcafe5029e2f49f7518a1b103c454aab008ea38f0e8af4ff65732f662deeb4ded0426973185935fbf4948db0b93abd2f9b0945c02c3179284667f57abad4
6
+ metadata.gz: 04dc39a5fdc6e5d809686e9d0b5fc8468771722ff4b50fd8cf644e9baa9be89815213facb0dee695c7d832c7d45c04f1090837300eddd3bb7e95020fa62a4900
7
+ data.tar.gz: faf0afd4dc392a14c44fcf91d34d631f8c819aa85561296839246346ef429015df0fa16d5d6427f931da1ea2960b0177c45f07ff070b4299aa11282d878b759c
@@ -8,7 +8,7 @@ module Smplkit
8
8
  # iterations may add SIEM exports as additional sub-clients
9
9
  # (ADR-047 §2.7 lists SIEM streaming as a Pro-tier capability).
10
10
  class AuditClient
11
- attr_reader :events
11
+ attr_reader :events, :forwarders, :functions
12
12
 
13
13
  def initialize(api_key:, base_url:, timeout: 10.0)
14
14
  cfg = SmplkitGeneratedClient::Audit::Configuration.new
@@ -17,8 +17,11 @@ module Smplkit
17
17
  cfg.access_token = api_key
18
18
  cfg.timeout = timeout
19
19
  api_client = SmplkitGeneratedClient::Audit::ApiClient.new(cfg)
20
- api = SmplkitGeneratedClient::Audit::EventsApi.new(api_client)
21
- @events = Events.new(api)
20
+ events_api = SmplkitGeneratedClient::Audit::EventsApi.new(api_client)
21
+ forwarders_api = SmplkitGeneratedClient::Audit::ForwardersApi.new(api_client)
22
+ @events = Events.new(events_api)
23
+ @forwarders = Forwarders.new(forwarders_api)
24
+ @functions = Functions.new(forwarders_api)
22
25
  end
23
26
 
24
27
  def _close
@@ -19,7 +19,8 @@ module Smplkit
19
19
  # with +smpl.+ are rejected by the server with a 403 (the buffer
20
20
  # logs and drops permanent failures).
21
21
  def record(action:, resource_type:, resource_id:,
22
- occurred_at: nil, snapshot: nil, data: nil, idempotency_key: nil)
22
+ occurred_at: nil, snapshot: nil, data: nil, idempotency_key: nil,
23
+ do_not_forward: false)
23
24
  raise ArgumentError, "action is required" if action.nil? || action.to_s.empty?
24
25
  raise ArgumentError, "resource_type is required" if resource_type.nil? || resource_type.to_s.empty?
25
26
  raise ArgumentError, "resource_id is required" if resource_id.nil? || resource_id.to_s.empty?
@@ -45,7 +46,8 @@ module Smplkit
45
46
  resource_id: resource_id,
46
47
  occurred_at: normalized_occurred_at,
47
48
  snapshot: snapshot,
48
- data: data || {}
49
+ data: data || {},
50
+ do_not_forward: do_not_forward
49
51
  )
50
52
  resource = SmplkitGeneratedClient::Audit::EventResource.new(
51
53
  id: "",
@@ -117,7 +119,7 @@ module Smplkit
117
119
  :id, :action, :resource_type, :resource_id,
118
120
  :occurred_at, :created_at,
119
121
  :actor_type, :actor_id, :actor_label,
120
- :snapshot, :data, :idempotency_key,
122
+ :snapshot, :data, :idempotency_key, :do_not_forward,
121
123
  keyword_init: true
122
124
  ) do
123
125
  def self.from_resource(resource)
@@ -134,7 +136,8 @@ module Smplkit
134
136
  actor_label: attrs.actor_label,
135
137
  snapshot: attrs.snapshot,
136
138
  data: attrs.data || {},
137
- idempotency_key: attrs.idempotency_key
139
+ idempotency_key: attrs.idempotency_key,
140
+ do_not_forward: attrs.do_not_forward || false
138
141
  )
139
142
  end
140
143
  end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smplkit
4
+ module Audit
5
+ # SIEM streaming forwarders for the authenticated account.
6
+ #
7
+ # Pro tier only — every method here raises a wrapped 402
8
+ # +SmplkitGeneratedClient::Audit::ApiError+ on lower tiers.
9
+ class Forwarders
10
+ attr_reader :deliveries, :actions
11
+
12
+ def initialize(api)
13
+ @api = api
14
+ @deliveries = ForwarderDeliveries.new(api)
15
+ @actions = ForwarderActions.new(api)
16
+ end
17
+
18
+ def create(name:, forwarder_type:, http:, enabled: true,
19
+ filter: nil, transform: nil, data: nil)
20
+ body = wrap_forwarder(nil, name, forwarder_type, http, enabled, filter, transform, data)
21
+ resp = @api.create_forwarder(body)
22
+ Forwarder.from_resource(resp.data)
23
+ end
24
+
25
+ def list(forwarder_type: nil, enabled: nil, page_size: nil, page_after: nil)
26
+ opts = {}
27
+ opts[:filter_forwarder_type] = forwarder_type if forwarder_type
28
+ opts[:filter_enabled] = enabled unless enabled.nil?
29
+ opts[:page_size] = page_size if page_size
30
+ opts[:page_after] = page_after if page_after
31
+ resp = @api.list_forwarders(opts)
32
+ forwarders = (resp.data || []).map { |r| Forwarder.from_resource(r) }
33
+ ListForwardersPage.new(forwarders, Forwarders.next_cursor(resp.links&._next))
34
+ end
35
+
36
+ def get(forwarder_id)
37
+ resp = @api.get_forwarder(forwarder_id)
38
+ Forwarder.from_resource(resp.data)
39
+ end
40
+
41
+ def update(forwarder_id, name:, forwarder_type:, http:, enabled: true,
42
+ filter: nil, transform: nil, data: nil)
43
+ body = wrap_forwarder(forwarder_id, name, forwarder_type, http, enabled, filter, transform, data)
44
+ resp = @api.update_forwarder(forwarder_id, body)
45
+ Forwarder.from_resource(resp.data)
46
+ end
47
+
48
+ def delete(forwarder_id)
49
+ @api.delete_forwarder(forwarder_id)
50
+ nil
51
+ end
52
+
53
+ def self.next_cursor(link)
54
+ return nil unless link.is_a?(String)
55
+
56
+ idx = link.index("page[after]=")
57
+ return nil if idx.nil?
58
+
59
+ token = link[(idx + "page[after]=".length)..]
60
+ amp = token.index("&")
61
+ amp ? token[0...amp] : token
62
+ end
63
+
64
+ private
65
+
66
+ def wrap_forwarder(id, name, forwarder_type, http, enabled, filter, transform, data)
67
+ attrs = SmplkitGeneratedClient::Audit::Forwarder.new(
68
+ name: name,
69
+ forwarder_type: forwarder_type,
70
+ enabled: enabled,
71
+ # Server-side validation rejects ``data: null`` (the field is
72
+ # required-non-null in the OpenAPI schema). Default to an empty
73
+ # hash mirroring the AuditEvents.record fix.
74
+ data: data || {},
75
+ http: ForwarderHttp.to_wire(http),
76
+ filter: filter,
77
+ transform: transform
78
+ )
79
+ resource = SmplkitGeneratedClient::Audit::ForwarderResource.new(
80
+ id: id ? id.to_s : "",
81
+ type: "forwarder",
82
+ attributes: attrs
83
+ )
84
+ SmplkitGeneratedClient::Audit::ForwarderResponse.new(data: resource)
85
+ end
86
+ end
87
+
88
+ # Sub-namespace for the per-forwarder delivery log + per-delivery retry.
89
+ class ForwarderDeliveries
90
+ attr_reader :actions
91
+
92
+ def initialize(api)
93
+ @api = api
94
+ @actions = DeliveryActions.new(api)
95
+ end
96
+
97
+ def list(forwarder_id, status: nil, created_at_range: nil, page_size: nil, page_after: nil)
98
+ opts = {}
99
+ opts[:filter_status] = status if status
100
+ opts[:filter_created_at] = created_at_range if created_at_range
101
+ opts[:page_size] = page_size if page_size
102
+ opts[:page_after] = page_after if page_after
103
+ resp = @api.list_forwarder_deliveries(forwarder_id, opts)
104
+ deliveries = (resp.data || []).map { |r| ForwarderDelivery.from_resource(r) }
105
+ ListDeliveriesPage.new(deliveries, Forwarders.next_cursor(resp.links&._next))
106
+ end
107
+ end
108
+
109
+ # +client.audit.forwarders.deliveries.actions.retry(forwarder_id, delivery_id)+
110
+ class DeliveryActions
111
+ def initialize(api)
112
+ @api = api
113
+ end
114
+
115
+ def retry(forwarder_id, delivery_id)
116
+ resp = @api.retry_forwarder_delivery(forwarder_id, delivery_id)
117
+ ForwarderDelivery.from_resource(resp.data)
118
+ end
119
+ end
120
+
121
+ # +client.audit.forwarders.actions.retry_failed_deliveries(forwarder_id)+
122
+ class ForwarderActions
123
+ def initialize(api)
124
+ @api = api
125
+ end
126
+
127
+ def retry_failed_deliveries(forwarder_id)
128
+ resp = @api.retry_failed_forwarder_deliveries(forwarder_id)
129
+ RetryFailedDeliveriesSummary.new(
130
+ attempted: resp.attempted,
131
+ succeeded: resp.succeeded,
132
+ failed: resp.failed
133
+ )
134
+ end
135
+ end
136
+
137
+ # ----------------------------------------------------------------------
138
+ # Public-facing model structs
139
+ # ----------------------------------------------------------------------
140
+
141
+ HttpHeader = Struct.new(:name, :value, keyword_init: true)
142
+
143
+ # rubocop:disable Lint/StructNewOverride -- ``:method`` matches the
144
+ # API attribute and shadowing Struct#method is the expected ergonomics.
145
+ ForwarderHttp = Struct.new(:method, :url, :headers, :body, :success_status, keyword_init: true) do
146
+ def initialize(method: "POST", url: "", headers: nil, body: nil, success_status: "2xx")
147
+ super(method: method, url: url, headers: headers || [], body: body, success_status: success_status)
148
+ end
149
+
150
+ def self.to_wire(src)
151
+ h = src.is_a?(Hash) ? new(**src) : src
152
+ SmplkitGeneratedClient::Audit::ForwarderHttp.new(
153
+ method: h.method,
154
+ url: h.url,
155
+ headers: (h.headers || []).map do |hdr|
156
+ name, value = if hdr.is_a?(Hash)
157
+ [hdr[:name] || hdr["name"],
158
+ hdr[:value] || hdr["value"]]
159
+ else
160
+ [hdr.name, hdr.value]
161
+ end
162
+ SmplkitGeneratedClient::Audit::HttpHeader.new(name: name, value: value)
163
+ end,
164
+ body: h.body,
165
+ success_status: h.success_status
166
+ )
167
+ end
168
+
169
+ def self.from_wire(src)
170
+ return new if src.nil?
171
+
172
+ new(
173
+ method: src.method || "POST",
174
+ url: src.url || "",
175
+ headers: (src.headers || []).map { |h| HttpHeader.new(name: h.name, value: h.value) },
176
+ body: src.body,
177
+ success_status: src.success_status || "2xx"
178
+ )
179
+ end
180
+ end
181
+ # rubocop:enable Lint/StructNewOverride
182
+
183
+ # rubocop:disable Lint/StructNewOverride -- ``:filter`` matches the
184
+ # API attribute and shadowing Struct#filter is the expected ergonomics.
185
+ Forwarder = Struct.new(
186
+ :id, :name, :slug, :forwarder_type, :enabled,
187
+ :filter, :transform, :http, :data,
188
+ :created_at, :updated_at, :deleted_at, :version,
189
+ keyword_init: true
190
+ ) do
191
+ def self.from_resource(resource)
192
+ a = resource.attributes
193
+ new(
194
+ id: resource.id,
195
+ name: a.name,
196
+ slug: a.slug,
197
+ forwarder_type: a.forwarder_type,
198
+ enabled: a.enabled.nil? || a.enabled,
199
+ filter: a.filter,
200
+ transform: a.transform,
201
+ http: ForwarderHttp.from_wire(a.http),
202
+ data: a.data || {},
203
+ created_at: a.created_at,
204
+ updated_at: a.updated_at,
205
+ deleted_at: a.deleted_at,
206
+ version: a.version
207
+ )
208
+ end
209
+ end
210
+ # rubocop:enable Lint/StructNewOverride
211
+
212
+ ListForwardersPage = Struct.new(:forwarders, :next_cursor)
213
+
214
+ ForwarderDelivery = Struct.new(
215
+ :id, :forwarder_id, :event_id, :attempt_number, :status,
216
+ :request, :response_status, :response_body, :latency_ms, :error, :created_at,
217
+ keyword_init: true
218
+ ) do
219
+ def self.from_resource(resource)
220
+ a = resource.attributes
221
+ new(
222
+ id: resource.id,
223
+ forwarder_id: a.forwarder_id,
224
+ event_id: a.event_id,
225
+ attempt_number: a.attempt_number,
226
+ status: a.status,
227
+ request: a.request,
228
+ response_status: a.response_status,
229
+ response_body: a.response_body,
230
+ latency_ms: a.latency_ms,
231
+ error: a.error,
232
+ created_at: a.created_at
233
+ )
234
+ end
235
+ end
236
+
237
+ ListDeliveriesPage = Struct.new(:deliveries, :next_cursor)
238
+
239
+ RetryFailedDeliveriesSummary = Struct.new(:attempted, :succeeded, :failed, keyword_init: true)
240
+
241
+ TestForwarderResult = Struct.new(
242
+ :succeeded, :response_status, :response_headers, :response_body, :latency_ms, :error,
243
+ keyword_init: true
244
+ )
245
+ end
246
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smplkit
4
+ module Audit
5
+ # +client.audit.functions.test_forwarder.actions.execute(...)+
6
+ class Functions
7
+ attr_reader :test_forwarder
8
+
9
+ def initialize(api)
10
+ @test_forwarder = TestForwarderNamespace.new(api)
11
+ end
12
+ end
13
+
14
+ # Sub-namespace for the test_forwarder action.
15
+ class TestForwarderNamespace
16
+ attr_reader :actions
17
+
18
+ def initialize(api)
19
+ @actions = TestForwarderActions.new(api)
20
+ end
21
+ end
22
+
23
+ # +execute+ is a server-side proxy that lets the console preview a
24
+ # destination without browser CORS getting in the way. The audit
25
+ # service applies its SSRF guard before resolving the URL —
26
+ # private/loopback/link-local addresses (incl. the EC2 IMDS at
27
+ # +169.254.169.254+) and disallowed ports are rejected.
28
+ class TestForwarderActions
29
+ def initialize(api)
30
+ @api = api
31
+ end
32
+
33
+ def execute(url:, method: "POST", headers: nil, body: nil,
34
+ success_status: "2xx", timeout_ms: nil)
35
+ req = SmplkitGeneratedClient::Audit::TestForwarderRequest.new(
36
+ url: url,
37
+ method: method,
38
+ headers: (headers || []).map do |h|
39
+ name, value = h.is_a?(Hash) ? [h[:name] || h["name"], h[:value] || h["value"]] : [h.name, h.value]
40
+ SmplkitGeneratedClient::Audit::HttpHeader.new(name: name, value: value)
41
+ end,
42
+ body: body,
43
+ success_status: success_status,
44
+ timeout_ms: timeout_ms
45
+ )
46
+ resp = @api.execute_test_forwarder(req)
47
+ TestForwarderResult.new(
48
+ succeeded: resp.succeeded || false,
49
+ response_status: resp.response_status,
50
+ response_headers: resp.response_headers || {},
51
+ response_body: resp.response_body || "",
52
+ latency_ms: resp.latency_ms,
53
+ error: resp.error
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/smplkit.rb CHANGED
@@ -64,6 +64,8 @@ require_relative "smplkit/management/buffer"
64
64
  require_relative "smplkit/management/client"
65
65
  require_relative "smplkit/audit/buffer"
66
66
  require_relative "smplkit/audit/events"
67
+ require_relative "smplkit/audit/forwarders"
68
+ require_relative "smplkit/audit/functions"
67
69
  require_relative "smplkit/audit/client"
68
70
  require_relative "smplkit/client"
69
71
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smplkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.23
4
+ version: 1.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Smpl Solutions LLC
@@ -583,6 +583,8 @@ files:
583
583
  - lib/smplkit/audit/buffer.rb
584
584
  - lib/smplkit/audit/client.rb
585
585
  - lib/smplkit/audit/events.rb
586
+ - lib/smplkit/audit/forwarders.rb
587
+ - lib/smplkit/audit/functions.rb
586
588
  - lib/smplkit/client.rb
587
589
  - lib/smplkit/config/client.rb
588
590
  - lib/smplkit/config/helpers.rb