connectors 0.1.0 → 0.1.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/CHANGELOG.md +16 -0
- data/CONNECTORS_FRAMEWORK.md +54 -99
- data/MCP_CLIENT.md +5 -5
- data/README.md +5 -3
- data/app/connectors/gmail/api.rb +6 -14
- data/app/connectors/gmail/connector.rb +11 -24
- data/app/connectors/gmail/mime_builder.rb +0 -3
- data/app/connectors/gmail/mime_parser.rb +1 -2
- data/app/connectors/gmail/polling.rb +7 -14
- data/app/connectors/resend/connector.rb +2 -4
- data/app/controllers/connectors/actions_controller.rb +0 -6
- data/app/controllers/connectors/credentials_controller.rb +10 -23
- data/app/controllers/connectors/grants_controller.rb +6 -11
- data/app/controllers/connectors/types_controller.rb +18 -45
- data/app/controllers/connectors/webhooks_controller.rb +2 -4
- data/app/jobs/connectors/deliver_webhook_job.rb +2 -4
- data/app/models/connectors/credential_share.rb +3 -10
- data/app/models/connectors/grant.rb +2 -4
- data/config/routes.rb +6 -13
- data/db/migrate/20260518210324_create_connectors_grants.rb +4 -11
- data/db/migrate/20260521140000_create_connectors_credential_shares.rb +2 -8
- data/docs/releasing.md +4 -4
- data/lib/connectors/action.rb +4 -7
- data/lib/connectors/action_builder.rb +5 -12
- data/lib/connectors/action_runner.rb +5 -11
- data/lib/connectors/auth_injection.rb +7 -18
- data/lib/connectors/client_builder.rb +4 -10
- data/lib/connectors/configuration.rb +9 -39
- data/lib/connectors/connector.rb +31 -83
- data/lib/connectors/credential_schema.rb +12 -29
- data/lib/connectors/credential_tester.rb +3 -4
- data/lib/connectors/credential_type_registry.rb +9 -20
- data/lib/connectors/credential_types/http_auth.rb +18 -55
- data/lib/connectors/engine.rb +3 -6
- data/lib/connectors/errors.rb +2 -5
- data/lib/connectors/middleware/authenticate_generic.rb +4 -14
- data/lib/connectors/middleware/pre_authentication.rb +2 -13
- data/lib/connectors/oauth/authorize_url.rb +3 -9
- data/lib/connectors/oauth/client_credentials.rb +0 -4
- data/lib/connectors/oauth/pkce.rb +3 -9
- data/lib/connectors/oauth/revoke.rb +2 -6
- data/lib/connectors/oauth1.rb +4 -8
- data/lib/connectors/permission_check.rb +2 -4
- data/lib/connectors/poll_runner.rb +1 -5
- data/lib/connectors/pre_authentication_helpers.rb +4 -9
- data/lib/connectors/version.rb +1 -1
- data/lib/connectors/webhook_context.rb +8 -18
- data/lib/connectors/webhook_lifecycle.rb +1 -8
- data/lib/connectors/webhook_methods.rb +2 -6
- data/openapi.yaml +16 -16
- metadata +21 -1
|
@@ -6,13 +6,13 @@ module Connectors
|
|
|
6
6
|
# # config/initializers/connectors.rb
|
|
7
7
|
# Connectors.configure do |c|
|
|
8
8
|
# c.owner_class_name = "User"
|
|
9
|
-
# c.current_owner_resolver = ->(controller) { controller.
|
|
9
|
+
# c.current_owner_resolver = ->(controller) { controller.request.env["connectors.current_owner"] }
|
|
10
10
|
# c.host_base_url = ENV.fetch("APP_BASE_URL", "http://localhost:3000")
|
|
11
11
|
# c.oauth_credentials = {
|
|
12
12
|
# slack: { client_id: ENV["SLACK_CLIENT_ID"], client_secret: ENV["SLACK_CLIENT_SECRET"] },
|
|
13
13
|
# linear: { client_id: ENV["LINEAR_CLIENT_ID"], client_secret: ENV["LINEAR_CLIENT_SECRET"] }
|
|
14
14
|
# }
|
|
15
|
-
# c.on_webhook = ->(event) {
|
|
15
|
+
# c.on_webhook = ->(event) { ProcessWebhookJob.perform_later(event.id) }
|
|
16
16
|
# end
|
|
17
17
|
class Configuration
|
|
18
18
|
attr_accessor :owner_class_name,
|
|
@@ -58,39 +58,17 @@ module Connectors
|
|
|
58
58
|
current_owner_resolver.call(controller)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# principal_id]` pairs representing every identity the requester has
|
|
64
|
-
# for sharing-visibility purposes.
|
|
61
|
+
# Resolve managed secrets through the host callback. Returned values
|
|
62
|
+
# override stored credentials for the lifetime of the Grant instance.
|
|
65
63
|
#
|
|
66
|
-
#
|
|
67
|
-
# hosts override to include teams / projects:
|
|
68
|
-
#
|
|
69
|
-
# c.principal_resolver = ->(ctrl) {
|
|
70
|
-
# [
|
|
71
|
-
# ["User", ctrl.current_user.id],
|
|
72
|
-
# *ctrl.current_user.team_ids.map { |id| ["Team", id] },
|
|
73
|
-
# ]
|
|
74
|
-
# }
|
|
75
|
-
# n8n parity: external secrets EE module
|
|
76
|
-
# (`external-secrets.controller.ee.ts`). Host wires a block that takes a
|
|
77
|
-
# Grant (one whose `is_managed?` is true) and returns the hash to merge
|
|
78
|
-
# into the credentials at request time. Returned values overwrite the
|
|
79
|
-
# DB-stored values for the lifetime of that Grant instance.
|
|
80
|
-
#
|
|
81
|
-
# c.secrets_resolver = ->(grant) {
|
|
82
|
-
# vault_client.read("kv/data/connectors/#{grant.connector_key}/#{grant.external_ref}")
|
|
83
|
-
# }
|
|
64
|
+
# c.secrets_resolver = ->(grant) { vault_client.read(grant.external_ref) }
|
|
84
65
|
def resolve_secrets(grant)
|
|
85
66
|
return {} if secrets_resolver.nil?
|
|
86
67
|
secrets_resolver.call(grant).to_h.transform_keys(&:to_s)
|
|
87
68
|
end
|
|
88
69
|
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# editor can hide / lock those fields. n8n parity:
|
|
92
|
-
# `frontend.service.ts:681-705` — same shape, just emitted server-side
|
|
93
|
-
# instead of recomputed in the editor.
|
|
70
|
+
# Vault-sourced field names exposed as __overwritten_properties
|
|
71
|
+
# so clients can display the fields as managed.
|
|
94
72
|
def managed_fields_for(connector_key)
|
|
95
73
|
return [] if secrets_managed_fields_for.nil?
|
|
96
74
|
Array(secrets_managed_fields_for.call(connector_key)).map(&:to_s)
|
|
@@ -103,16 +81,8 @@ module Connectors
|
|
|
103
81
|
[ [ owner.class.name, owner.id ] ]
|
|
104
82
|
end
|
|
105
83
|
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
# render HTML → close popup). For split frontend/backend setups
|
|
109
|
-
# (Activepieces-style: provider → frontend → POST exchange → backend),
|
|
110
|
-
# set this to the FRONTEND's callback page, e.g.
|
|
111
|
-
# `"http://localhost:3001/oauth/callback"`.
|
|
112
|
-
#
|
|
113
|
-
# Either way it must MATCH a value registered in the provider's app
|
|
114
|
-
# console; if it doesn't, Google/Slack/etc. respond with
|
|
115
|
-
# `redirect_uri_mismatch` before consent even renders.
|
|
84
|
+
# Use the configured frontend callback URL or the mounted engine callback.
|
|
85
|
+
# Register the resulting URL with the OAuth provider.
|
|
116
86
|
def resolved_app_callback_url(connector_key)
|
|
117
87
|
return app_callback_url if app_callback_url.present?
|
|
118
88
|
base = host_base_url or
|
data/lib/connectors/connector.rb
CHANGED
|
@@ -71,8 +71,7 @@ module Connectors
|
|
|
71
71
|
@rate_limit_config = { limit: count, per: per }
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
# Declarative
|
|
75
|
-
# (packages/workflow/src/interfaces.ts:340-348). The test fires a real
|
|
74
|
+
# Declarative connection test. Fires a real
|
|
76
75
|
# HTTP request via the connector's middleware stack (so credentials are
|
|
77
76
|
# injected the same way they would be at runtime) and applies the
|
|
78
77
|
# configured rules to decide pass/fail.
|
|
@@ -99,10 +98,6 @@ module Connectors
|
|
|
99
98
|
}
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
# n8n-style declarative auth injection (mirrors `IAuthenticateGeneric`
|
|
103
|
-
# at packages/workflow/src/interfaces.ts:278-288 + the shape of
|
|
104
|
-
# `IRequestOptionsSimplifiedAuth` at :197-208).
|
|
105
|
-
#
|
|
106
101
|
# `properties` accepts any subset of:
|
|
107
102
|
# headers: { "Authorization" => "=Bearer {{$credentials.api_key}}" }
|
|
108
103
|
# qs: { "api_key" => "={{$credentials.api_key}}" }
|
|
@@ -118,12 +113,12 @@ module Connectors
|
|
|
118
113
|
# headers: { "Authorization" => "=Bearer {{$credentials.api_key}}" }
|
|
119
114
|
# }
|
|
120
115
|
#
|
|
121
|
-
# When declared, this replaces the
|
|
116
|
+
# When declared, this replaces the imperative `api_key_in` configuration
|
|
122
117
|
# at runtime — both the schema serializer and the Faraday client
|
|
123
118
|
# honor the declarative form first, falling back to `api_key_in` only
|
|
124
119
|
# if `authenticate` is absent.
|
|
125
120
|
def authenticate(type:, properties:)
|
|
126
|
-
raise ArgumentError, "only type: :generic is supported
|
|
121
|
+
raise ArgumentError, "only type: :generic is supported" \
|
|
127
122
|
unless type.to_sym == :generic
|
|
128
123
|
@authenticate_config = { "type" => "generic", "properties" => properties }
|
|
129
124
|
end
|
|
@@ -136,15 +131,8 @@ module Connectors
|
|
|
136
131
|
@authenticate_config || @credential_schema&.resolved_authenticate
|
|
137
132
|
end
|
|
138
133
|
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
# credentials look stale (`expires_at` missing or in the past).
|
|
142
|
-
# Returns a hash merged into the grant's credentials before the
|
|
143
|
-
# AuthenticateGeneric step injects them into the request.
|
|
144
|
-
#
|
|
145
|
-
# Reference impl: CrowdStrikeOAuth2Api.credentials.ts:62-76 —
|
|
146
|
-
# fetches a session token from /oauth2/token using client_id +
|
|
147
|
-
# client_secret, returns `{ session_token, expires_at }`.
|
|
134
|
+
# Run before outgoing requests when expires_at is absent or past.
|
|
135
|
+
# Merge returned credentials before authentication injection.
|
|
148
136
|
#
|
|
149
137
|
# pre_authentication do |credentials, helpers|
|
|
150
138
|
# response = helpers.http_request(
|
|
@@ -162,13 +150,11 @@ module Connectors
|
|
|
162
150
|
|
|
163
151
|
attr_reader :pre_authentication_block
|
|
164
152
|
|
|
165
|
-
# Imperative API-key
|
|
166
|
-
#
|
|
167
|
-
# declarative `authenticate` DSL above. When both are declared,
|
|
168
|
-
# `authenticate` wins.
|
|
153
|
+
# Imperative API-key injection. A declared authenticate block takes
|
|
154
|
+
# precedence over this setting.
|
|
169
155
|
#
|
|
170
156
|
# api_key_in :header, name: "Authorization", prefix: "token "
|
|
171
|
-
# api_key_in :query,
|
|
157
|
+
# api_key_in :query, name: "api_key"
|
|
172
158
|
def api_key_in(location, name:, prefix: nil)
|
|
173
159
|
@api_key_options = { location: location, name: name, prefix: prefix }
|
|
174
160
|
end
|
|
@@ -178,14 +164,13 @@ module Connectors
|
|
|
178
164
|
# Declares OAuth2 provider endpoints + default scope. Client id/secret
|
|
179
165
|
# are supplied by the host via Connectors.configuration.oauth_credentials.
|
|
180
166
|
#
|
|
181
|
-
# `grant_type:`
|
|
182
|
-
# selects the runtime flow:
|
|
167
|
+
# `grant_type:` selects the runtime flow:
|
|
183
168
|
# - "authorizationCode" (default) — standard redirect flow
|
|
184
169
|
# - "clientCredentials" — server-to-server, no user redirect
|
|
185
170
|
# - "pkce" — RFC 7636 with S256 challenge
|
|
186
171
|
#
|
|
187
172
|
# `authentication:` (header | body) — how client credentials are sent
|
|
188
|
-
# to the token endpoint.
|
|
173
|
+
# to the token endpoint. Default
|
|
189
174
|
# `"header"` (HTTP Basic).
|
|
190
175
|
#
|
|
191
176
|
# oauth2 authorize_url: "https://slack.com/oauth/v2/authorize",
|
|
@@ -214,8 +199,7 @@ module Connectors
|
|
|
214
199
|
|
|
215
200
|
# Declares OAuth1.0a provider endpoints + consumer signature method.
|
|
216
201
|
# Consumer key/secret come from `Connectors.configuration.oauth_credentials`
|
|
217
|
-
#
|
|
218
|
-
# semantically identical in n8n's storage too).
|
|
202
|
+
# using the same client_id / client_secret configuration keys as OAuth2.
|
|
219
203
|
#
|
|
220
204
|
# oauth1 request_token_url: "https://api.twitter.com/oauth/request_token",
|
|
221
205
|
# authorize_url: "https://api.twitter.com/oauth/authorize",
|
|
@@ -257,10 +241,8 @@ module Connectors
|
|
|
257
241
|
|
|
258
242
|
attr_reader :revoke_token_block
|
|
259
243
|
|
|
260
|
-
#
|
|
261
|
-
#
|
|
262
|
-
# editor hides the "Use external secret" toggle AND the API refuses
|
|
263
|
-
# POSTs that pass `is_managed: true`. Surfaces on the types endpoint.
|
|
244
|
+
# Disable managed-credential creation in the catalog and API.
|
|
245
|
+
# Requests passing is_managed: true are rejected for this type.
|
|
264
246
|
def skip_managed_creation!
|
|
265
247
|
@skip_managed_creation = true
|
|
266
248
|
end
|
|
@@ -269,16 +251,10 @@ module Connectors
|
|
|
269
251
|
@skip_managed_creation == true
|
|
270
252
|
end
|
|
271
253
|
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
# generic HTTP-Request node's picker. Most provider-specific connectors
|
|
275
|
-
# leave this off; generic HTTP types (HttpBearerAuth etc.) flip it on.
|
|
276
|
-
# Connector-level call so non-`extends` connectors can opt in directly:
|
|
254
|
+
# Enable generic HTTP-request credential use. The setting is also
|
|
255
|
+
# inherited from base credential schemas.
|
|
277
256
|
#
|
|
278
257
|
# generic_auth!
|
|
279
|
-
#
|
|
280
|
-
# When the connector `extends` an Http*Auth schema, the schema's flag
|
|
281
|
-
# already propagates — but this stays available as a per-connector override.
|
|
282
258
|
def generic_auth!
|
|
283
259
|
@generic_auth = true
|
|
284
260
|
end
|
|
@@ -288,14 +264,10 @@ module Connectors
|
|
|
288
264
|
@credential_schema&.generic_auth? || false
|
|
289
265
|
end
|
|
290
266
|
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
# means "any node may use it" — same default as n8n.
|
|
267
|
+
# Restrict credential use to listed node types; an empty list is unrestricted.
|
|
268
|
+
# Hosts enforce this through Connectors::PermissionCheck.permit!.
|
|
294
269
|
#
|
|
295
270
|
# supported_nodes :slack_send_message, :slack_get_channel
|
|
296
|
-
#
|
|
297
|
-
# The check itself is `Connectors::PermissionCheck.permit!` — call it
|
|
298
|
-
# from your node's execution wrapper to enforce.
|
|
299
271
|
def supported_nodes(*names)
|
|
300
272
|
if names.empty?
|
|
301
273
|
@supported_nodes || []
|
|
@@ -304,11 +276,7 @@ module Connectors
|
|
|
304
276
|
end
|
|
305
277
|
end
|
|
306
278
|
|
|
307
|
-
#
|
|
308
|
-
# (interfaces.ts:380; type `ICredentialHttpRequestNode` at :350-354).
|
|
309
|
-
# Advertises the connector to the generic HTTP-Request node's
|
|
310
|
-
# "credential picker" UI — the user sees "Linear API" alongside the
|
|
311
|
-
# docs link + a pre-filled base URL.
|
|
279
|
+
# Optional label, documentation and base URL for an HTTP credential picker.
|
|
312
280
|
#
|
|
313
281
|
# http_request_node name: "Linear API",
|
|
314
282
|
# docs_url: "https://developers.linear.app/",
|
|
@@ -319,8 +287,7 @@ module Connectors
|
|
|
319
287
|
# + `docs_url:` + one of the base-URL slots.
|
|
320
288
|
return @http_request_node if name.nil? && docs_url.nil?
|
|
321
289
|
unless api_base_url || api_base_url_placeholder
|
|
322
|
-
raise ArgumentError, "http_request_node requires either api_base_url or api_base_url_placeholder
|
|
323
|
-
"(n8n's `ICredentialHttpRequestNode` union, interfaces.ts:350-354)"
|
|
290
|
+
raise ArgumentError, "http_request_node requires either api_base_url or api_base_url_placeholder"
|
|
324
291
|
end
|
|
325
292
|
@http_request_node = {
|
|
326
293
|
"name" => name,
|
|
@@ -331,11 +298,8 @@ module Connectors
|
|
|
331
298
|
}.compact
|
|
332
299
|
end
|
|
333
300
|
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
# picker render time): if `supportedNodes` is non-empty, the requesting
|
|
337
|
-
# node must be in it. The generic HTTP-Request node bypasses this
|
|
338
|
-
# check entirely when `genericAuth` is true on the credential type.
|
|
301
|
+
# An empty supported_nodes list allows any node. Otherwise require an
|
|
302
|
+
# allowlist match, with an exception for :http_request when generic_auth is set.
|
|
339
303
|
def supports_node?(node_type)
|
|
340
304
|
node_type = node_type.to_sym
|
|
341
305
|
return true if supported_nodes.empty? # no allowlist → permitted
|
|
@@ -399,12 +363,9 @@ module Connectors
|
|
|
399
363
|
(@webhook_groups || {}).keys
|
|
400
364
|
end
|
|
401
365
|
|
|
402
|
-
#
|
|
403
|
-
#
|
|
404
|
-
#
|
|
405
|
-
# The block runs once per scheduler tick (or once per manual POST to
|
|
406
|
-
# /poll), receives the per-grant scratch hash for cursor persistence,
|
|
407
|
-
# and returns the new items.
|
|
366
|
+
# Runs once per host-scheduled poll and returns new items. PollRunner
|
|
367
|
+
# supplies cursor state scoped to the instance_key, or to the grant
|
|
368
|
+
# when no instance_key is provided. The host owns scheduling.
|
|
408
369
|
#
|
|
409
370
|
# polling do |grant, static_data|
|
|
410
371
|
# since = static_data["last_id"]
|
|
@@ -412,9 +373,6 @@ module Connectors
|
|
|
412
373
|
# static_data["last_id"] = items.first["id"] if items.any?
|
|
413
374
|
# items
|
|
414
375
|
# end
|
|
415
|
-
#
|
|
416
|
-
# The scheduler itself is a workflow-roadmap concern; the connector
|
|
417
|
-
# side just provides the block + cursor contract.
|
|
418
376
|
def polling(&block)
|
|
419
377
|
@polling_block = block
|
|
420
378
|
end
|
|
@@ -464,12 +422,8 @@ module Connectors
|
|
|
464
422
|
end
|
|
465
423
|
end
|
|
466
424
|
|
|
467
|
-
#
|
|
468
|
-
#
|
|
469
|
-
# (packages/workflow/src/interfaces.ts NodeProperties) and Activepieces'
|
|
470
|
-
# `createAction(...)`. Each action gets a typed param schema, an
|
|
471
|
-
# optional output schema, and an execute block that runs in the
|
|
472
|
-
# connector instance's context.
|
|
425
|
+
# Declare a provider operation with input fields, optional output fields
|
|
426
|
+
# and an execute block evaluated in the connector instance.
|
|
473
427
|
#
|
|
474
428
|
# action :send_email,
|
|
475
429
|
# display_name: "Send Email",
|
|
@@ -521,10 +475,8 @@ module Connectors
|
|
|
521
475
|
@grant = grant
|
|
522
476
|
end
|
|
523
477
|
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
# declares an `authenticate` block, AuthenticateGeneric middleware
|
|
527
|
-
# applies it; otherwise the imperative `auth_scheme` is used.
|
|
478
|
+
# Build the Faraday middleware stack. A connector or inherited schema
|
|
479
|
+
# authenticate block takes precedence over the imperative auth_scheme.
|
|
528
480
|
def client
|
|
529
481
|
@client ||= ClientBuilder.new(
|
|
530
482
|
base_url: self.class.base_url,
|
|
@@ -546,13 +498,9 @@ module Connectors
|
|
|
546
498
|
# the connector opts into polling.
|
|
547
499
|
def poll; end
|
|
548
500
|
|
|
549
|
-
# Override
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
-
# `.webhook_name`, `.signature`, `.grant`, plus `.event` for direct
|
|
553
|
-
# access to the persisted `Connectors::WebhookEvent`. The context
|
|
554
|
-
# delegates `.payload_hash` / `.payload` for back-compat with handlers
|
|
555
|
-
# written before Phase 7 — the same method works on both.
|
|
501
|
+
# Override to process a WebhookContext from DeliverWebhookJob.
|
|
502
|
+
# The context exposes body, headers, query, raw_body, webhook_name,
|
|
503
|
+
# signature and grant, plus the persisted event and payload aliases.
|
|
556
504
|
def handle_webhook(ctx); end
|
|
557
505
|
|
|
558
506
|
# Convenience — validates the grant's credentials against the connector's
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
#
|
|
3
|
-
# contain. Mirrors n8n's `INodeProperties` shape exactly (n8n source:
|
|
4
|
-
# packages/workflow/src/interfaces.ts:1773-1812) so a single frontend
|
|
5
|
-
# renderer handles every connector with no per-connector logic.
|
|
2
|
+
# Credential form schema used by client renderers.
|
|
6
3
|
#
|
|
7
4
|
# credentials do
|
|
8
5
|
# field :api_key,
|
|
@@ -17,12 +14,9 @@ module Connectors
|
|
|
17
14
|
# Inheritance: a connector can `extends :oauth2` (or any other registered
|
|
18
15
|
# base type) and `field` calls in the block can REDECLARE inherited fields
|
|
19
16
|
# — typically as `type: "hidden"` with a fixed `default:` to lock provider
|
|
20
|
-
# endpoints
|
|
21
|
-
# SlackOAuth2Api.credentials.ts:38-122).
|
|
17
|
+
# endpoints.
|
|
22
18
|
class CredentialSchema
|
|
23
|
-
#
|
|
24
|
-
# the most commonly-used subset; later waves add resourceLocator /
|
|
25
|
-
# collection / fixedCollection / dateTime as connectors demand them.
|
|
19
|
+
# Supported credential form field types.
|
|
26
20
|
ALLOWED_TYPES = %w[
|
|
27
21
|
string number boolean
|
|
28
22
|
options multiOptions
|
|
@@ -43,9 +37,7 @@ module Connectors
|
|
|
43
37
|
(type_options || {}).any? { |k, v| k.to_s == "password" && v == true }
|
|
44
38
|
end
|
|
45
39
|
|
|
46
|
-
#
|
|
47
|
-
# (`displayName`, `typeOptions`); we mirror that so the editor's
|
|
48
|
-
# generic form renderer eats it without translation.
|
|
40
|
+
# Serialize form metadata with camelCase field keys for client renderers.
|
|
49
41
|
def to_property
|
|
50
42
|
{
|
|
51
43
|
name: name.to_s,
|
|
@@ -90,7 +82,7 @@ module Connectors
|
|
|
90
82
|
end
|
|
91
83
|
|
|
92
84
|
# Inherit fields from one or more registered base credential schemas
|
|
93
|
-
#
|
|
85
|
+
# such as :oauth2 or :http_bearer_auth. Redeclare inherited fields via
|
|
94
86
|
# `field` — typically as `type: "hidden", default: "..."` to lock
|
|
95
87
|
# provider-specific endpoints. Doubles as a getter when called with
|
|
96
88
|
# no args (the serializer needs to read it back).
|
|
@@ -99,20 +91,15 @@ module Connectors
|
|
|
99
91
|
@extends = base_names.flatten.map(&:to_sym)
|
|
100
92
|
end
|
|
101
93
|
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
# at :278-288). Per-credential-type so any connector that `extends` this
|
|
105
|
-
# schema inherits the injection contract automatically.
|
|
94
|
+
# Declare authentication properties inherited by connectors extending
|
|
95
|
+
# this credential schema.
|
|
106
96
|
def authenticate(type: nil, properties: nil)
|
|
107
97
|
return @authenticate if type.nil? && properties.nil?
|
|
108
98
|
raise ArgumentError, "authenticate type: must be :generic" unless type.to_sym == :generic
|
|
109
99
|
@authenticate = { "type" => "generic", "properties" => properties }
|
|
110
100
|
end
|
|
111
101
|
|
|
112
|
-
#
|
|
113
|
-
# Request node's credential picker (`interfaces.ts:379`; example
|
|
114
|
-
# consumer `HttpBearerAuth.credentials.ts:12`). Stored here so the
|
|
115
|
-
# serializer surfaces it; runtime enforcement lands in Phase 5.
|
|
102
|
+
# Allow generic HTTP-request use and advertise it in the catalog.
|
|
116
103
|
def generic_auth!
|
|
117
104
|
@generic_auth = true
|
|
118
105
|
end
|
|
@@ -176,10 +163,8 @@ module Connectors
|
|
|
176
163
|
@fields.values
|
|
177
164
|
end
|
|
178
165
|
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
# frontend doesn't need to fetch each parent type separately. Children
|
|
182
|
-
# override parents by re-declaring with the same name.
|
|
166
|
+
# Resolve inherited fields server-side. Children replace parent fields
|
|
167
|
+
# with the same name, so clients receive a complete form schema.
|
|
183
168
|
def resolved_fields
|
|
184
169
|
parent_fields = @extends.flat_map { |name| CredentialTypeRegistry.fetch(name).own_fields }
|
|
185
170
|
union = {}
|
|
@@ -187,10 +172,8 @@ module Connectors
|
|
|
187
172
|
union.values
|
|
188
173
|
end
|
|
189
174
|
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
# chain declared one. Matches n8n's runtime credential-walker pattern
|
|
193
|
-
# at packages/cli/src/credential-types.ts:26-37.
|
|
175
|
+
# Resolve the nearest authenticate declaration, preferring this schema.
|
|
176
|
+
# Returns nil when no schema in the inheritance chain declares one.
|
|
194
177
|
def resolved_authenticate
|
|
195
178
|
return @authenticate if @authenticate
|
|
196
179
|
@extends.each do |name|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module Connectors
|
|
2
2
|
# Executes a connector's `test_request` declaration against the live
|
|
3
|
-
# provider, applying configured rules to decide pass/fail.
|
|
4
|
-
# `CredentialsTester` (packages/cli/src/services/credentials-tester.service.ts).
|
|
3
|
+
# provider, applying configured rules to decide pass/fail.
|
|
5
4
|
#
|
|
6
5
|
# result = CredentialTester.run(grant)
|
|
7
6
|
# # => { status: "OK", message: "Connection successful" }
|
|
@@ -9,8 +8,8 @@ module Connectors
|
|
|
9
8
|
# # => { status: "Error", message: "Slack token is invalid", details: {...} }
|
|
10
9
|
#
|
|
11
10
|
# Reuses the connector's Faraday client so the same auth middleware,
|
|
12
|
-
# auto-refresh, rate-limit
|
|
13
|
-
#
|
|
11
|
+
# auto-refresh, rate-limit and error normalization apply. A successful
|
|
12
|
+
# test verifies this request, not permissions for every provider operation.
|
|
14
13
|
class CredentialTester
|
|
15
14
|
OK = "OK".freeze
|
|
16
15
|
ERROR = "Error".freeze
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
# Registry of reusable
|
|
3
|
-
#
|
|
4
|
-
# canonical `OAuth2Api` credential type (packages/nodes-base/credentials/
|
|
5
|
-
# OAuth2Api.credentials.ts:1-238). The registry is populated at engine
|
|
6
|
-
# boot — see `Connectors::Engine`.
|
|
2
|
+
# Registry of reusable credential schemas populated at engine boot.
|
|
3
|
+
# Connectors inherit registered schemas through extends.
|
|
7
4
|
class CredentialTypeRegistry
|
|
8
5
|
class << self
|
|
9
6
|
def register(name, schema)
|
|
@@ -31,10 +28,8 @@ module Connectors
|
|
|
31
28
|
end
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# fixed `default`. Field set matches n8n's OAuth2Api credentials type
|
|
37
|
-
# (packages/nodes-base/credentials/OAuth2Api.credentials.ts:1-238).
|
|
31
|
+
# Base OAuth2 form. Provider connectors can override fields with hidden
|
|
32
|
+
# defaults to fix their endpoints and other configuration.
|
|
38
33
|
OAUTH2 = CredentialSchema.build do
|
|
39
34
|
field :grant_type,
|
|
40
35
|
type: "options",
|
|
@@ -95,10 +90,8 @@ module Connectors
|
|
|
95
90
|
{ name: "Body", value: "body" }
|
|
96
91
|
]
|
|
97
92
|
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# (no runtime JWE decryption); ship the schema so the editor can
|
|
101
|
-
# render the configuration UI for enterprise SSO scenarios.
|
|
93
|
+
# JWE configuration metadata for form rendering only.
|
|
94
|
+
# The engine does not decrypt JWE tokens.
|
|
102
95
|
field :jwe_enabled,
|
|
103
96
|
type: "boolean",
|
|
104
97
|
display_name: "Encrypted Tokens (JWE)",
|
|
@@ -114,15 +107,11 @@ module Connectors
|
|
|
114
107
|
display_options: { show: { jwe_enabled: [ true ] } }
|
|
115
108
|
end
|
|
116
109
|
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# storage shape as OAuth2's `client_id` / `client_secret` — they map to
|
|
120
|
-
# the `consumerKey` / `consumerSecret` here. Connectors that extend this
|
|
121
|
-
# type generally `field :authorization_url, type: "hidden", default: "..."`
|
|
122
|
-
# to lock the provider endpoint.
|
|
110
|
+
# Base OAuth1 form. Client ID and secret represent the consumer key
|
|
111
|
+
# and secret. Provider connectors can fix endpoint fields with hidden defaults.
|
|
123
112
|
OAUTH1 = CredentialSchema.build do
|
|
124
113
|
display_name "OAuth1 API"
|
|
125
|
-
documentation_url "
|
|
114
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
126
115
|
generic_auth!
|
|
127
116
|
|
|
128
117
|
field :authorization_url,
|
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# (the runtime check lands in Phase 5).
|
|
7
|
-
#
|
|
8
|
-
# Connectors inherit any of these via `extends :http_bearer_auth` etc.
|
|
9
|
-
# When a connector extends one of the runtime-supported types
|
|
10
|
-
# (basic / bearer / header / query), the AuthenticateGeneric middleware
|
|
11
|
-
# picks up the inherited `authenticate` block and injects per-request —
|
|
12
|
-
# no `api_key_in` boilerplate needed.
|
|
13
|
-
#
|
|
14
|
-
# n8n source: `packages/nodes-base/credentials/HttpBasicAuth.credentials.ts`,
|
|
15
|
-
# `HttpBearerAuth.credentials.ts`, `HttpHeaderAuth.credentials.ts`,
|
|
16
|
-
# `HttpQueryAuth.credentials.ts`, `HttpDigestAuth.credentials.ts`,
|
|
17
|
-
# `HttpCustomAuth.credentials.ts`.
|
|
2
|
+
# Reusable HTTP authentication schemas. Basic, bearer, header and query
|
|
3
|
+
# types declare runtime injection inherited by extending connectors.
|
|
4
|
+
# Digest and custom JSON types provide form metadata only.
|
|
5
|
+
# All types advertise generic_auth for HTTP-request credential selection.
|
|
18
6
|
module CredentialTypes
|
|
19
|
-
#
|
|
20
|
-
# n8n: HttpBasicAuth.credentials.ts:1-33. n8n's HTTP node implements
|
|
21
|
-
# Basic auth itself (no `authenticate` block on the credential). We
|
|
22
|
-
# synthesize one using AuthenticateGeneric's `auth: { username, password }`
|
|
23
|
-
# shortcut so the declarative path works end-to-end.
|
|
7
|
+
# Basic authentication via the declarative username/password shortcut.
|
|
24
8
|
HTTP_BASIC_AUTH = CredentialSchema.build do
|
|
25
9
|
display_name "Basic Auth"
|
|
26
|
-
documentation_url "
|
|
10
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
27
11
|
generic_auth!
|
|
28
12
|
|
|
29
13
|
field :user,
|
|
@@ -41,11 +25,10 @@ module Connectors
|
|
|
41
25
|
}
|
|
42
26
|
end
|
|
43
27
|
|
|
44
|
-
#
|
|
45
|
-
# n8n: HttpBearerAuth.credentials.ts:1-43.
|
|
28
|
+
# Bearer token authentication.
|
|
46
29
|
HTTP_BEARER_AUTH = CredentialSchema.build do
|
|
47
30
|
display_name "Bearer Auth"
|
|
48
|
-
documentation_url "
|
|
31
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
49
32
|
generic_auth!
|
|
50
33
|
|
|
51
34
|
field :token,
|
|
@@ -65,13 +48,10 @@ module Connectors
|
|
|
65
48
|
}
|
|
66
49
|
end
|
|
67
50
|
|
|
68
|
-
#
|
|
69
|
-
# n8n: HttpHeaderAuth.credentials.ts:1-45. Both header NAME and VALUE
|
|
70
|
-
# are templated — AuthInjection.walk resolves keys as well as values
|
|
71
|
-
# (see auth_injection.rb).
|
|
51
|
+
# Both header name and value are resolved from credential templates.
|
|
72
52
|
HTTP_HEADER_AUTH = CredentialSchema.build do
|
|
73
53
|
display_name "Header Auth"
|
|
74
|
-
documentation_url "
|
|
54
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
75
55
|
generic_auth!
|
|
76
56
|
|
|
77
57
|
field :name,
|
|
@@ -94,12 +74,10 @@ module Connectors
|
|
|
94
74
|
}
|
|
95
75
|
end
|
|
96
76
|
|
|
97
|
-
#
|
|
98
|
-
# n8n: HttpQueryAuth.credentials.ts:1-30. n8n's HTTP node handles
|
|
99
|
-
# injection imperatively; we synthesize the equivalent generic block.
|
|
77
|
+
# Query parameter authentication.
|
|
100
78
|
HTTP_QUERY_AUTH = CredentialSchema.build do
|
|
101
79
|
display_name "Query Auth"
|
|
102
|
-
documentation_url "
|
|
80
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
103
81
|
generic_auth!
|
|
104
82
|
|
|
105
83
|
field :name,
|
|
@@ -117,19 +95,10 @@ module Connectors
|
|
|
117
95
|
}
|
|
118
96
|
end
|
|
119
97
|
|
|
120
|
-
#
|
|
121
|
-
# n8n: HttpDigestAuth.credentials.ts:1-32. n8n consumes this via axios's
|
|
122
|
-
# built-in digest challenge handling. Faraday has no equivalent
|
|
123
|
-
# middleware out of the box — we ship the SCHEMA (so the frontend can
|
|
124
|
-
# render the form + a future generic HTTP-Request node can offer the
|
|
125
|
-
# type) but the AuthenticateGeneric middleware can't perform the
|
|
126
|
-
# challenge-response handshake on its own.
|
|
127
|
-
#
|
|
128
|
-
# Runtime support requires a Faraday-Digest middleware. Tracked as a
|
|
129
|
-
# follow-up; for now the type is form-only.
|
|
98
|
+
# Digest credential form only; no challenge-response runtime is provided.
|
|
130
99
|
HTTP_DIGEST_AUTH = CredentialSchema.build do
|
|
131
100
|
display_name "Digest Auth"
|
|
132
|
-
documentation_url "
|
|
101
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
133
102
|
generic_auth!
|
|
134
103
|
|
|
135
104
|
field :user,
|
|
@@ -142,20 +111,14 @@ module Connectors
|
|
|
142
111
|
default: "",
|
|
143
112
|
secret: true
|
|
144
113
|
|
|
145
|
-
# No authenticate block
|
|
146
|
-
# (Faraday-Digest gem or equivalent). Phase 2 ships the schema only.
|
|
114
|
+
# No authenticate block: digest challenge handling is not implemented.
|
|
147
115
|
end
|
|
148
116
|
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
# `{ headers, body, qs }`. n8n's HTTP node parses this JSON at request
|
|
152
|
-
# time and applies it. Our AuthenticateGeneric middleware works on a
|
|
153
|
-
# static `properties` block; runtime injection of a user-supplied JSON
|
|
154
|
-
# is deferred until a Custom-API-Call style node lands (workflow
|
|
155
|
-
# roadmap). Phase 2 ships the schema only.
|
|
117
|
+
# Custom JSON credential form only; user-supplied JSON is not
|
|
118
|
+
# applied to outbound requests by this schema.
|
|
156
119
|
HTTP_CUSTOM_AUTH = CredentialSchema.build do
|
|
157
120
|
display_name "Custom Auth"
|
|
158
|
-
documentation_url "
|
|
121
|
+
documentation_url "https://github.com/jackhelio/connectors/blob/main/CONNECTORS_FRAMEWORK.md#6-built-in-base-credential-types"
|
|
159
122
|
generic_auth!
|
|
160
123
|
|
|
161
124
|
field :json,
|