@voxgig/sdkgen 4.19.0 → 4.20.0
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.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/AgentGuideContent.js +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +6 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/testkit.js +2 -2
- package/dist/testkit.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +4 -1
- package/dist/utility.js +60 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +10 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +20 -20
- package/project/.sdk/src/cmp/c/Config_c.ts +15 -0
- package/project/.sdk/src/cmp/c/Main_c.ts +15 -0
- package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +371 -0
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +47 -1
- package/project/.sdk/src/cmp/clojure/Main_clojure.ts +11 -1
- package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +374 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +40 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +17 -0
- package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +441 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +42 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +392 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +36 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +15 -0
- package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +418 -0
- package/project/.sdk/src/cmp/go/Config_go.ts +12 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +14 -0
- package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +322 -0
- package/project/.sdk/src/cmp/java/Config_java.ts +20 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +14 -0
- package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +298 -0
- package/project/.sdk/src/cmp/js/Config_js.ts +12 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +6 -0
- package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +244 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +23 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +20 -0
- package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +440 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +15 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +11 -0
- package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +364 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +31 -1
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +19 -0
- package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +454 -0
- package/project/.sdk/src/cmp/perl/Config_perl.ts +30 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +14 -0
- package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +387 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +12 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +7 -0
- package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +325 -0
- package/project/.sdk/src/cmp/py/Config_py.ts +12 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +12 -0
- package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +389 -0
- package/project/.sdk/src/cmp/rb/Config_rb.ts +18 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +10 -0
- package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +305 -0
- package/project/.sdk/src/cmp/rust/Config_rust.ts +41 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +16 -0
- package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +410 -0
- package/project/.sdk/src/cmp/scala/Config_scala.ts +21 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +14 -0
- package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +415 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +37 -1
- package/project/.sdk/src/cmp/swift/Main_swift.ts +10 -0
- package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +439 -0
- package/project/.sdk/src/cmp/ts/Config_ts.ts +12 -1
- package/project/.sdk/src/cmp/ts/Main_ts.ts +5 -0
- package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +237 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +33 -1
- package/project/.sdk/src/cmp/zig/Main_zig.ts +16 -0
- package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +545 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +31 -20
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +36 -40
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +11 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -32
- package/project/.sdk/tm/java/utility/MakeOptions.java +13 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +14 -1
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +13 -46
- package/project/.sdk/tm/ocaml/Makefile +11 -1
- package/project/.sdk/tm/ocaml/sdk_helpers.ml +15 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +31 -25
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +125 -12
- package/project/.sdk/tm/ocaml/test/t_pipeline.ml +64 -6
- package/project/.sdk/tm/perl/utility/make_options.pm +10 -1
- package/project/.sdk/tm/php/utility/MakeOptions.php +8 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +13 -1
- package/project/.sdk/tm/scala/utility/Make.scala +14 -1
- package/project/.sdk/tm/scala/utility/Prepare.scala +16 -40
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +30 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +21 -39
- package/project/.sdk/tm/zig/core/utility.zig +37 -45
- package/project/.sdk/tm/zig/test/pipeline_test.zig +34 -0
- package/project/sdkgen-package.json +1 -1
- package/src/cmp/AgentGuideContent.ts +1 -1
- package/src/sdkgen.ts +4 -1
- package/src/testkit.ts +2 -2
- package/src/utility.ts +75 -0
- package/project/.sdk/tm/c/utility/prepare_auth.c +0 -65
- package/project/.sdk/tm/csharp/utility/PrepareAuth.cs +0 -54
- package/project/.sdk/tm/go/utility/prepare_auth.go +0 -59
- package/project/.sdk/tm/java/utility/PrepareAuth.java +0 -66
- package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +0 -49
- package/project/.sdk/tm/lua/utility/prepare_auth.lua +0 -52
- package/project/.sdk/tm/perl/utility/prepare_auth.pm +0 -56
- package/project/.sdk/tm/php/utility/PrepareAuth.php +0 -45
- package/project/.sdk/tm/py/pkg/utility/prepare_auth.py +0 -46
- package/project/.sdk/tm/rb/utility/prepare_auth.rb +0 -35
- package/project/.sdk/tm/rust/utility/prepare_auth.rs +0 -65
- package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +0 -78
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
Content,
|
|
4
|
+
File,
|
|
5
|
+
Folder,
|
|
6
|
+
cmp,
|
|
7
|
+
isAuthSuppressed,
|
|
8
|
+
isHttpBasicAuth,
|
|
9
|
+
resolveAuthIn,
|
|
10
|
+
resolveAuthName,
|
|
11
|
+
resolveAuthPrefix,
|
|
12
|
+
} from '@voxgig/sdkgen'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
|
|
16
|
+
// rather than templated. The lua peer of PrepareAuth_ts.
|
|
17
|
+
//
|
|
18
|
+
// This was a static file at tm/lua/utility/prepare_auth.lua that hardcoded
|
|
19
|
+
// a lower-case "authorization" HEADER. apidef has always resolved the
|
|
20
|
+
// scheme's "in" and "name" into main.kit.info.security - joplin's says
|
|
21
|
+
// in: "query", name: "token" - and generation dropped both. The result was
|
|
22
|
+
// an SDK that sent a header the API does not read and never sent the query
|
|
23
|
+
// parameter it does, so it could not authenticate at all. Four repos in
|
|
24
|
+
// the cedar fleet shipped that way: joplin (token), pipedrive (api_token),
|
|
25
|
+
// trello (key), lm-umbrella (apiKey).
|
|
26
|
+
//
|
|
27
|
+
// A template cannot fix this, because the three placements need three
|
|
28
|
+
// different bodies and a template has to pick one. A component emits the
|
|
29
|
+
// branch this API actually uses and nothing else - no dead query code in a
|
|
30
|
+
// bearer-token SDK, and no runtime branch on a value that is fixed at
|
|
31
|
+
// generation time.
|
|
32
|
+
//
|
|
33
|
+
// The header placement renders BYTE-FOR-BYTE what the template rendered,
|
|
34
|
+
// so every header-based lua SDK regenerates with a zero diff.
|
|
35
|
+
const PrepareAuth = cmp(async function PrepareAuth(props: any) {
|
|
36
|
+
const { target } = props
|
|
37
|
+
const { model } = props.ctx$
|
|
38
|
+
|
|
39
|
+
// `!isAuthSuppressed`, NOT `isAuthActive`. The latter is also false when
|
|
40
|
+
// the SPEC merely declares no security scheme (`main.kit.info.auth:
|
|
41
|
+
// false`), and those SDKs still carry a credential: optspec always
|
|
42
|
+
// declares `apikey` and makeOptions fills `options.auth` from its
|
|
43
|
+
// defaults, so the runtime guard never fired and they have always sent
|
|
44
|
+
// it. Only an explicit `main.kit.config.auth.active: false` means "no
|
|
45
|
+
// credential, ever", which is what isAuthSuppressed reads.
|
|
46
|
+
const active = !isAuthSuppressed(model)
|
|
47
|
+
const where = resolveAuthIn(model)
|
|
48
|
+
const prefix = resolveAuthPrefix(model)
|
|
49
|
+
const basic = isHttpBasicAuth(model)
|
|
50
|
+
|
|
51
|
+
// A HEADER NAME IS LOWER-CASED HERE; A QUERY OR COOKIE NAME IS NOT.
|
|
52
|
+
// Every header key the lua SDK writes is lower case (make_spec's
|
|
53
|
+
// "content-type", the secrets feature's "authorization", fetcher's
|
|
54
|
+
// lower-casing of the response headers), and HTTP header names are
|
|
55
|
+
// case-insensitive on the wire - so "Authorization" must arrive here as
|
|
56
|
+
// the "authorization" the rest of the SDK looks up. A query parameter
|
|
57
|
+
// and a cookie name are case-SENSITIVE: lm-umbrella's "apiKey" is not
|
|
58
|
+
// "apikey", and lower-casing it would break exactly the APIs this
|
|
59
|
+
// change exists to fix.
|
|
60
|
+
const cred = resolveAuthName(model)
|
|
61
|
+
const name = 'header' === where ? cred.toLowerCase() : cred
|
|
62
|
+
|
|
63
|
+
// FOLDER NESTING. Main_lua opens NO folder around this call: its
|
|
64
|
+
// Folder({name:'.'}) around Config is the target root itself, and the
|
|
65
|
+
// lua tree has no src/ wrapper (tm/lua/src holds only feature
|
|
66
|
+
// placeholders and Main's Copy excludes it). The template lived at
|
|
67
|
+
// tm/lua/utility/prepare_auth.lua, which the blanket Copy lands at
|
|
68
|
+
// <root>/utility/prepare_auth.lua - the one path
|
|
69
|
+
// require("utility.prepare_auth") in utility/register.lua resolves. So
|
|
70
|
+
// this component opens the single "utility" folder that path needs.
|
|
71
|
+
// Opening a second one would write <root>/utility/utility/, where
|
|
72
|
+
// nothing requires it.
|
|
73
|
+
Folder({ name: 'utility' }, () => {
|
|
74
|
+
File({ name: 'prepare_auth.' + target.ext }, () => {
|
|
75
|
+
Content(render({
|
|
76
|
+
project: model.const.Name,
|
|
77
|
+
active,
|
|
78
|
+
where,
|
|
79
|
+
name,
|
|
80
|
+
prefix,
|
|
81
|
+
basic,
|
|
82
|
+
}))
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
function render(spec: {
|
|
89
|
+
project: string, active: boolean, where: string,
|
|
90
|
+
name: string, prefix: string, basic: boolean
|
|
91
|
+
}): string {
|
|
92
|
+
const head = `-- ${spec.project} SDK utility: prepare_auth\n`
|
|
93
|
+
|
|
94
|
+
// NO AUTH AT ALL. A public API's SDK gets a prepare_auth that is honest
|
|
95
|
+
// about it rather than one that deletes a header nobody set.
|
|
96
|
+
if (!spec.active) {
|
|
97
|
+
return head + `
|
|
98
|
+
-- This API declares no authentication, so there is no credential to place.
|
|
99
|
+
-- The function stays in the pipeline because make_spec calls it
|
|
100
|
+
-- unconditionally.
|
|
101
|
+
local function prepare_auth_util(ctx)
|
|
102
|
+
local spec = ctx.spec
|
|
103
|
+
if spec == nil then
|
|
104
|
+
return nil, ctx:make_error("auth_no_spec",
|
|
105
|
+
"Expected context spec property to be defined.")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
return spec, nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
return prepare_auth_util
|
|
112
|
+
`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const where = spec.where
|
|
116
|
+
|
|
117
|
+
// HTTP Basic is header-only by definition: the scheme is
|
|
118
|
+
// "Authorization: Basic base64(user:pass)". It cannot be expressed as a
|
|
119
|
+
// query parameter or a cookie, so the branch is emitted only where it
|
|
120
|
+
// can mean something.
|
|
121
|
+
const basic = spec.basic && 'header' === where
|
|
122
|
+
|
|
123
|
+
const CRED = credConst(where)
|
|
124
|
+
|
|
125
|
+
const consts =
|
|
126
|
+
`local ${CRED} = "${luastr(spec.name)}"\n` +
|
|
127
|
+
('cookie' === where ? `local HEADER_COOKIE = "cookie"\n` : '') +
|
|
128
|
+
`local OPTION_APIKEY = "apikey"\n` +
|
|
129
|
+
(basic ? `local OPTION_SECRET = "secret"\n` : '') +
|
|
130
|
+
`local NOT_FOUND = "__NOTFOUND__"\n`
|
|
131
|
+
|
|
132
|
+
return head + `
|
|
133
|
+
local vs = require("utility.struct.struct")
|
|
134
|
+
|
|
135
|
+
` + consts + (basic ? BASE64 : '') + ('cookie' === where ? COOKIE_SET : '') + `
|
|
136
|
+
local function prepare_auth_util(ctx)
|
|
137
|
+
local spec = ctx.spec
|
|
138
|
+
if spec == nil then
|
|
139
|
+
return nil, ctx:make_error("auth_no_spec",
|
|
140
|
+
"Expected context spec property to be defined.")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
${bag(where)} local options = ctx.client:options_map()
|
|
144
|
+
|
|
145
|
+
-- Public APIs that need no auth omit the options.auth block entirely.
|
|
146
|
+
if options.auth == nil then
|
|
147
|
+
${clear(where, 4)}
|
|
148
|
+
return spec, nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
local apikey = vs.getprop(options, OPTION_APIKEY, NOT_FOUND)
|
|
152
|
+
` + (basic ? BASIC : '') + `
|
|
153
|
+
if apikey == nil
|
|
154
|
+
or (type(apikey) == "string" and (apikey == NOT_FOUND or apikey == ""))
|
|
155
|
+
then
|
|
156
|
+
${clear(where, 4)}
|
|
157
|
+
else
|
|
158
|
+
${place(where)}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
return spec, nil
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
return prepare_auth_util
|
|
165
|
+
`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
// The constant the credential name is bound to, named for where it goes -
|
|
170
|
+
// a reader of the generated file should not have to ask.
|
|
171
|
+
function credConst(where: string): string {
|
|
172
|
+
return 'query' === where ? 'QUERY_AUTH' :
|
|
173
|
+
'cookie' === where ? 'COOKIE_AUTH' : 'HEADER_AUTH'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// The bag the credential lands in, per placement. A cookie rides the
|
|
178
|
+
// header bag, because a cookie IS a header.
|
|
179
|
+
function bag(where: string): string {
|
|
180
|
+
return 'query' === where ?
|
|
181
|
+
' local query = spec.query\n' :
|
|
182
|
+
' local headers = spec.headers\n'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
function clear(where: string, indent: number): string {
|
|
187
|
+
const pad = ' '.repeat(indent)
|
|
188
|
+
|
|
189
|
+
if ('query' === where) {
|
|
190
|
+
return pad + 'query[QUERY_AUTH] = nil'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if ('cookie' === where) {
|
|
194
|
+
return pad + 'cookie_set(headers, nil)'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return pad + 'headers[HEADER_AUTH] = nil'
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
function place(where: string): string {
|
|
202
|
+
if ('query' === where) {
|
|
203
|
+
// NO PREFIX IN A QUERY STRING. "?token=Bearer%20abc" is not a thing any
|
|
204
|
+
// API reads; the prefix is a header-value convention and is dropped
|
|
205
|
+
// here deliberately rather than silently concatenated.
|
|
206
|
+
return ` -- NO PREFIX IN A QUERY STRING: "?token=Bearer%20abc" is not a thing
|
|
207
|
+
-- any API reads, so the auth.prefix a header placement space-joins is
|
|
208
|
+
-- dropped here deliberately rather than concatenated.
|
|
209
|
+
local apikey_val = ""
|
|
210
|
+
if type(apikey) == "string" then
|
|
211
|
+
apikey_val = apikey
|
|
212
|
+
end
|
|
213
|
+
query[QUERY_AUTH] = apikey_val`
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if ('cookie' === where) {
|
|
217
|
+
return ` local apikey_val = ""
|
|
218
|
+
if type(apikey) == "string" then
|
|
219
|
+
apikey_val = apikey
|
|
220
|
+
end
|
|
221
|
+
-- The prefix is a header-value convention and has no meaning in a
|
|
222
|
+
-- cookie pair, so it is dropped the way a query placement drops it.
|
|
223
|
+
cookie_set(headers, apikey_val)`
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// BYTE-FOR-BYTE the template's body, so a header-based SDK regenerates
|
|
227
|
+
// unchanged.
|
|
228
|
+
return ` local auth_prefix = ""
|
|
229
|
+
local ap = vs.getpath(options, "auth.prefix")
|
|
230
|
+
if type(ap) == "string" then
|
|
231
|
+
auth_prefix = ap
|
|
232
|
+
end
|
|
233
|
+
local apikey_val = ""
|
|
234
|
+
if type(apikey) == "string" then
|
|
235
|
+
apikey_val = apikey
|
|
236
|
+
end
|
|
237
|
+
-- Empty prefix (raw apiKey credential) must not add a leading space.
|
|
238
|
+
if auth_prefix == "" then
|
|
239
|
+
headers[HEADER_AUTH] = apikey_val
|
|
240
|
+
else
|
|
241
|
+
headers[HEADER_AUTH] = auth_prefix .. " " .. apikey_val
|
|
242
|
+
end`
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
// True HTTP Basic Auth: two credentials, base64-joined. Emitted only for a
|
|
247
|
+
// header placement (see render).
|
|
248
|
+
const BASIC = `
|
|
249
|
+
-- True HTTP Basic Auth needs TWO credentials, base64-joined - a single
|
|
250
|
+
-- token in the header (the branch below) can never authenticate against
|
|
251
|
+
-- an API that actually checks "Authorization: Basic base64(user:pass)".
|
|
252
|
+
if vs.getpath(options, "auth.basic") == true then
|
|
253
|
+
local secret = vs.getprop(options, OPTION_SECRET, NOT_FOUND)
|
|
254
|
+
|
|
255
|
+
if apikey == nil or secret == nil
|
|
256
|
+
or (type(apikey) == "string" and (apikey == NOT_FOUND or apikey == ""))
|
|
257
|
+
or (type(secret) == "string" and (secret == NOT_FOUND or secret == ""))
|
|
258
|
+
then
|
|
259
|
+
headers[HEADER_AUTH] = nil
|
|
260
|
+
else
|
|
261
|
+
local auth_prefix = ""
|
|
262
|
+
local ap = vs.getpath(options, "auth.prefix")
|
|
263
|
+
if type(ap) == "string" then
|
|
264
|
+
auth_prefix = ap
|
|
265
|
+
end
|
|
266
|
+
local joined = base64(tostring(apikey) .. ":" .. tostring(secret))
|
|
267
|
+
if auth_prefix == "" then
|
|
268
|
+
headers[HEADER_AUTH] = joined
|
|
269
|
+
else
|
|
270
|
+
headers[HEADER_AUTH] = auth_prefix .. " " .. joined
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
return spec, nil
|
|
275
|
+
end
|
|
276
|
+
`
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
// Lua 5.4 has no base64, and the vendored sekreto encoder lives inside the
|
|
280
|
+
// OPTIONAL secrets feature - an SDK that never asked for secrets does not
|
|
281
|
+
// ship it - so an HTTP Basic SDK carries its own. Emitted only in that
|
|
282
|
+
// branch, so nothing else pays for it.
|
|
283
|
+
const BASE64 = `
|
|
284
|
+
local B64_ALPHABET =
|
|
285
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
local function base64(text)
|
|
289
|
+
local out = {}
|
|
290
|
+
local size = #text
|
|
291
|
+
local index = 1
|
|
292
|
+
|
|
293
|
+
while index <= size do
|
|
294
|
+
local rest = size - index + 1
|
|
295
|
+
local a = string.byte(text, index)
|
|
296
|
+
local b = string.byte(text, index + 1) or 0
|
|
297
|
+
local c = string.byte(text, index + 2) or 0
|
|
298
|
+
local word = (a << 16) | (b << 8) | c
|
|
299
|
+
|
|
300
|
+
local chunk = {}
|
|
301
|
+
for shift = 18, 0, -6 do
|
|
302
|
+
local at = (word >> shift) & 0x3f
|
|
303
|
+
chunk[#chunk + 1] = string.sub(B64_ALPHABET, at + 1, at + 1)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
-- One trailing source byte yields two characters and "==", two yield
|
|
307
|
+
-- three and "=".
|
|
308
|
+
if rest == 1 then
|
|
309
|
+
chunk[3] = "="
|
|
310
|
+
chunk[4] = "="
|
|
311
|
+
elseif rest == 2 then
|
|
312
|
+
chunk[4] = "="
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
out[#out + 1] = table.concat(chunk)
|
|
316
|
+
index = index + 3
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
return table.concat(out)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
`
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
// A cookie header is SHARED: any other cookie the caller set rides in the
|
|
326
|
+
// same string, so the credential pair is rewritten in place and everything
|
|
327
|
+
// else is kept. A plain assignment would throw the caller's cookies away.
|
|
328
|
+
// A nil value removes just our pair.
|
|
329
|
+
const COOKIE_SET = `
|
|
330
|
+
local function cookie_set(headers, value)
|
|
331
|
+
local kept = {}
|
|
332
|
+
local existing = headers[HEADER_COOKIE]
|
|
333
|
+
|
|
334
|
+
if type(existing) == "string" then
|
|
335
|
+
for pair in string.gmatch(existing, "[^;]+") do
|
|
336
|
+
local one = string.match(pair, "^%s*(.-)%s*$")
|
|
337
|
+
if one ~= "" and string.match(one, "^[^=]*") ~= COOKIE_AUTH then
|
|
338
|
+
kept[#kept + 1] = one
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if value ~= nil then
|
|
344
|
+
kept[#kept + 1] = COOKIE_AUTH .. "=" .. value
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
if #kept == 0 then
|
|
348
|
+
headers[HEADER_COOKIE] = nil
|
|
349
|
+
else
|
|
350
|
+
headers[HEADER_COOKIE] = table.concat(kept, "; ")
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
`
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
function luastr(s: string): string {
|
|
358
|
+
return String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
export {
|
|
363
|
+
PrepareAuth
|
|
364
|
+
}
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
each,
|
|
9
9
|
isAuthActive,
|
|
10
10
|
isConfigData,
|
|
11
|
+
resolveAuthIn,
|
|
12
|
+
resolveAuthName,
|
|
11
13
|
resolveAuthPrefix,
|
|
12
14
|
targetFeatures,
|
|
13
15
|
} from '@voxgig/sdkgen'
|
|
@@ -148,7 +150,35 @@ const Config = cmp(async function Config(props: any) {
|
|
|
148
150
|
// server-variable defaults), which the hand-rolled build here omitted.
|
|
149
151
|
// Passing target.name opts this target into the main slug/version/target
|
|
150
152
|
// identity fields (read by station's descriptor - see configDefinition).
|
|
151
|
-
const { def: config
|
|
153
|
+
const { def: config } = configDefinition(model, target.name)
|
|
154
|
+
|
|
155
|
+
// `in` and `name` TRAVEL WITH THE PREFIX NOW. apidef resolved both from the
|
|
156
|
+
// spec's securityScheme all along and generation dropped them, so an
|
|
157
|
+
// apiKey-in-query API got an `authorization` header it does not read.
|
|
158
|
+
//
|
|
159
|
+
// Added to the shared `def` here rather than in configDefinition, so this
|
|
160
|
+
// change moves ocaml and nothing else - and both representations below
|
|
161
|
+
// render from the SAME object, which is why the JSON is stringified AFTER
|
|
162
|
+
// this rather than taken from configDefinition: the data rung and the
|
|
163
|
+
// literal rung must not be able to describe different configs.
|
|
164
|
+
//
|
|
165
|
+
// ONLY WHEN THEY DIFFER from the header/Authorization default, so every
|
|
166
|
+
// header-based SDK's sdk_config.ml is byte-identical to what it generated
|
|
167
|
+
// before. `options.auth` is absent entirely when auth is inactive
|
|
168
|
+
// (configDefinition's own gate), and there is nothing to qualify then.
|
|
169
|
+
const authIn = resolveAuthIn(model)
|
|
170
|
+
const authName = resolveAuthName(model)
|
|
171
|
+
const authOpts = (config as any).options?.auth
|
|
172
|
+
if (null != authOpts) {
|
|
173
|
+
if ('header' !== authIn) {
|
|
174
|
+
authOpts.in = authIn
|
|
175
|
+
}
|
|
176
|
+
if ('Authorization' !== authName) {
|
|
177
|
+
authOpts.name = authName
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const configJson = JSON.stringify(config)
|
|
152
182
|
const asData = isConfigData(configJson, configReprSetting(model))
|
|
153
183
|
|
|
154
184
|
File({ name: 'sdk_config.' + target.ext }, () => {
|
|
@@ -24,6 +24,7 @@ import { Config, secretsBuild } from './Config_ocaml'
|
|
|
24
24
|
import { Gitignore } from './Gitignore_ocaml'
|
|
25
25
|
import { MainEntity } from './MainEntity_ocaml'
|
|
26
26
|
import { SdkError } from './SdkError_ocaml'
|
|
27
|
+
import { PrepareAuth } from './PrepareAuth_ocaml'
|
|
27
28
|
import { entityModule, ocamlString } from './utility_ocaml'
|
|
28
29
|
|
|
29
30
|
|
|
@@ -185,6 +186,24 @@ FEATURE_LINK =
|
|
|
185
186
|
Config({ target })
|
|
186
187
|
SdkError({ target })
|
|
187
188
|
|
|
189
|
+
// sdk_prepare_auth.ml is GENERATED, not templated: WHERE the credential
|
|
190
|
+
// goes (header / query / cookie, and under what name) is a fact about the
|
|
191
|
+
// API, and tm/ can only hold one answer. ocaml had no prepare_auth
|
|
192
|
+
// template to replace - the body was a `let` inside the monolithic
|
|
193
|
+
// tm/ocaml/sdk_runtime.ml - so this is a real extraction: the module
|
|
194
|
+
// keeps the `prepare_auth_util` name by aliasing this one, and every
|
|
195
|
+
// existing caller (the utility record, make_spec, the secrets feature's
|
|
196
|
+
// re-run, the shipped suites) resolves unchanged. See PrepareAuth_ocaml.
|
|
197
|
+
//
|
|
198
|
+
// AT ROOT LEVEL, with no Folder open. The ocaml tree is flat at the
|
|
199
|
+
// target root and the Makefile's `RUNTIME` names `sdk_prepare_auth.ml`
|
|
200
|
+
// by that exact path, between sdk_helpers.ml (which it uses) and
|
|
201
|
+
// sdk_runtime.ml (which uses it) - ocamlc compiles in the order it is
|
|
202
|
+
// handed and has no link-time reordering. Emitting it inside a Folder
|
|
203
|
+
// would put it where neither `RUNTIME` nor `-I .` looks, and
|
|
204
|
+
// sdk_runtime.ml would fail with `Unbound module Sdk_prepare_auth`.
|
|
205
|
+
PrepareAuth({ target })
|
|
206
|
+
|
|
188
207
|
// sdk_client.ml — the client constructors, direct/prepare, and the
|
|
189
208
|
// per-entity accessors (twin of the go root package + rust core/sdk.rs).
|
|
190
209
|
File({ name: 'sdk_client.' + target.ext }, () => {
|