@voxgig/sdkgen 4.10.0 → 4.11.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/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +330 -0
- package/project/.sdk/model/target/c.aon +15 -2
- package/project/.sdk/model/target/cpp.aon +18 -0
- package/project/.sdk/model/target/lua.aon +17 -0
- package/project/.sdk/model/target/ocaml.aon +48 -5
- package/project/.sdk/model/target/zig.aon +30 -4
- package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
- package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
- package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
- package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
- package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
- package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
- package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
- package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
- package/project/.sdk/tm/c/Makefile +58 -9
- package/project/.sdk/tm/c/core/sdk.h +10 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
- package/project/.sdk/tm/c/feature/secrets.c +797 -0
- package/project/.sdk/tm/c/feature/secrets.h +46 -0
- package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
- package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
- package/project/.sdk/tm/cpp/Makefile +48 -4
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
- package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
- package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
- package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
- package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
- package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
- package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
- package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
- package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
- package/project/.sdk/tm/lua/Makefile +15 -4
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
- package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
- package/project/.sdk/tm/ocaml/Makefile +52 -8
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
- package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
- package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
- package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
- package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
- package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
- package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
- package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
- package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
- package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
- package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
-- ProjectName SDK secrets feature
|
|
2
|
+
--
|
|
3
|
+
-- Secret access via a vendored @voxgig/sekreto provider chain, and the
|
|
4
|
+
-- access-token exchange some APIs require on top of it. The lua port of
|
|
5
|
+
-- tm/ts/src/feature/secrets/SecretsFeature.ts, seam for seam with the go
|
|
6
|
+
-- port (tm/go/feature/secrets_feature.go), whose structure this target
|
|
7
|
+
-- shares: a top-level feature/ container, synchronous hooks that cannot
|
|
8
|
+
-- fail an operation, and a transport that both raw paths cross.
|
|
9
|
+
--
|
|
10
|
+
-- The SDK's `apikey` option keeps exactly its old meaning: an explicit
|
|
11
|
+
-- credential given in code. This feature makes it ONE SOURCE among several
|
|
12
|
+
-- rather than the only one: when active, the apikey is resolved through a
|
|
13
|
+
-- sekreto chain in which the explicit option (when set) is the FIRST
|
|
14
|
+
-- provider - a `memory` store named `options` - so an explicit value always
|
|
15
|
+
-- wins, by sekreto's own first-hit rule rather than by special-case logic.
|
|
16
|
+
--
|
|
17
|
+
-- WHY THE TRANSPORT, NOT A HOOK. Every entity op runs feature_hook("PreSpec")
|
|
18
|
+
-- before make_spec builds the auth header, so a hook COULD resolve in time -
|
|
19
|
+
-- but utility/feature_hook.lua discards a hook's return value, so a PreSpec
|
|
20
|
+
-- hook can neither fail an operation nor be awaited, and `direct()` /
|
|
21
|
+
-- `graphql()` run no hooks at all. The transport wrapper installed in init
|
|
22
|
+
-- is the one seam every wire path crosses: entity ops (make_request), the
|
|
23
|
+
-- raw paths (_raw_request) and the exchange retries all come through it. It
|
|
24
|
+
-- resolves there, rewrites the authorization header prepare_auth built from
|
|
25
|
+
-- the options apikey, and - this is the point - REFUSES TO SEND while the
|
|
26
|
+
-- chain is broken.
|
|
27
|
+
--
|
|
28
|
+
-- MISS vs ERROR (sekreto's invariant): a provider MISS falls through - the
|
|
29
|
+
-- op proceeds, unauthenticated if nothing else supplies a credential. A
|
|
30
|
+
-- provider ERROR (unreachable vault, bad creds) must FAIL the op: a broken
|
|
31
|
+
-- vault never degrades into an unauthenticated request. The same gate holds
|
|
32
|
+
-- a construction failure: a `providers` entry that is not a provider or a
|
|
33
|
+
-- spec, or a kind this SDK was not generated with, is recorded at init and
|
|
34
|
+
-- every request is refused with sekreto's own message. Fail-closed at the
|
|
35
|
+
-- one seam every request must pass, and never a silently shortened chain.
|
|
36
|
+
--
|
|
37
|
+
-- THE RESOLVED CREDENTIAL IS FEATURE STATE, not an options write - go's
|
|
38
|
+
-- discipline, kept here for the same reason it is right there: the options
|
|
39
|
+
-- map is read raw on every request, and a feature that wrote into it would
|
|
40
|
+
-- be racing its own readers in a runtime with coroutines. The header the
|
|
41
|
+
-- wire sees is identical, because the wrapper builds it the way prepare_auth
|
|
42
|
+
-- does, from the same options.auth.prefix.
|
|
43
|
+
--
|
|
44
|
+
-- EXCHANGE: some APIs will not take a long-lived credential at all. What
|
|
45
|
+
-- the chain resolves is then a REFRESH token, which buys a short-lived
|
|
46
|
+
-- ACCESS token from a token endpoint (`exchange.path`, relative to
|
|
47
|
+
-- options.base); the access token is what every request carries, and when
|
|
48
|
+
-- a response status in `exchange.statuses` (401) says it is spent the
|
|
49
|
+
-- wrapper buys another and retries the same request once. Test mode buys
|
|
50
|
+
-- nothing and answers with a deterministic fake token.
|
|
51
|
+
--
|
|
52
|
+
-- PLUGIN KINDS. The four built-in kinds (env, memory, dotenv, file) come
|
|
53
|
+
-- with the vendored core; every other kind is a voxgig/plugin definition
|
|
54
|
+
-- the generated config_plugins module hands in, and only the groups the
|
|
55
|
+
-- model activated are in the tree at all. Those kinds reach
|
|
56
|
+
-- feature/secrets/sekreto/plugins/net.lua, which runs the compiled
|
|
57
|
+
-- transport helper (feature/secrets/native/sekreto-net) because Lua 5.4
|
|
58
|
+
-- has no sockets and no TLS - `make build` compiles it whenever a plugin
|
|
59
|
+
-- group is active, and never otherwise.
|
|
60
|
+
|
|
61
|
+
local vs = require("utility.struct.struct")
|
|
62
|
+
local json = require("dkjson")
|
|
63
|
+
local helpers = require("core.helpers")
|
|
64
|
+
local BaseFeature = require("feature.base_feature")
|
|
65
|
+
local sekreto = require("feature.secrets.sekreto")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
local SecretsFeature = {}
|
|
69
|
+
SecretsFeature.__index = SecretsFeature
|
|
70
|
+
setmetatable(SecretsFeature, { __index = BaseFeature })
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
-- Sekreto's own wording for a chain entry that is neither a provider nor a
|
|
74
|
+
-- spec (the dynamic-list refusal in the ts and kotlin constructors). The
|
|
75
|
+
-- lua constructor cannot be handed a non-table at all - a number entry
|
|
76
|
+
-- raises a bare "attempt to index" from inside sekreto, and a nil HOLE in
|
|
77
|
+
-- the list ends `ipairs` early and silently SHORTENS the chain - so the
|
|
78
|
+
-- classification is done here, with the constructor's message, and the
|
|
79
|
+
-- transport gate refuses to send. A shortened chain is exactly the
|
|
80
|
+
-- fail-open the gate exists to prevent.
|
|
81
|
+
local NOTAPROVIDER = "sekreto: not a provider or a provider spec: "
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
local function optstr(map, key, dflt)
|
|
85
|
+
local v = map ~= nil and map[key] or nil
|
|
86
|
+
if type(v) == "string" and v ~= "" then
|
|
87
|
+
return v
|
|
88
|
+
end
|
|
89
|
+
return dflt
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
local function optnum(map, key, dflt)
|
|
94
|
+
local v = map ~= nil and map[key] or nil
|
|
95
|
+
if type(v) == "number" then
|
|
96
|
+
return math.floor(v)
|
|
97
|
+
end
|
|
98
|
+
return dflt
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
-- The plugin DEFINITIONS the model selected for this feature, emitted by
|
|
103
|
+
-- Config_lua into config_plugins.lua from the catalogue's active
|
|
104
|
+
-- `plugin.def` entries. Upstream sekreto's contract since the registry
|
|
105
|
+
-- was retired: a kind not passed in `plugins` is unknown to that Sekreto,
|
|
106
|
+
-- so the model's choice of plugin groups IS the SDK's provider vocabulary.
|
|
107
|
+
--
|
|
108
|
+
-- Only a genuine module-not-found is tolerated (this template run against
|
|
109
|
+
-- the raw tm/ tree, with no generated config beside it): anything else is
|
|
110
|
+
-- a plugin module that failed to LOAD, and that must reach the gate rather
|
|
111
|
+
-- than quietly leave the chain without a kind it was configured with.
|
|
112
|
+
local function feature_plugins(name)
|
|
113
|
+
local ok, accessor = pcall(require, "config_plugins")
|
|
114
|
+
if not ok then
|
|
115
|
+
if type(accessor) == "string"
|
|
116
|
+
and accessor:find("module 'config_plugins' not found", 1, true) ~= nil
|
|
117
|
+
then
|
|
118
|
+
return {}, nil
|
|
119
|
+
end
|
|
120
|
+
return nil, accessor
|
|
121
|
+
end
|
|
122
|
+
if type(accessor) == "function" then
|
|
123
|
+
return accessor(name) or {}, nil
|
|
124
|
+
end
|
|
125
|
+
return {}, nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
function SecretsFeature.new()
|
|
130
|
+
local self = setmetatable(BaseFeature.new(), SecretsFeature)
|
|
131
|
+
self.version = "0.1.0"
|
|
132
|
+
self.name = "secrets"
|
|
133
|
+
self.active = true
|
|
134
|
+
self.client = nil
|
|
135
|
+
self.options = nil
|
|
136
|
+
|
|
137
|
+
-- The LIVE options table (the root context's, which is client.options):
|
|
138
|
+
-- read for `apikey` and for the `auth` suppression, never written.
|
|
139
|
+
self._liveopts = nil
|
|
140
|
+
|
|
141
|
+
self._secretname = "apikey"
|
|
142
|
+
self._cache = true
|
|
143
|
+
self._sek = nil
|
|
144
|
+
|
|
145
|
+
-- Exchange state: nil config when off; the refresh credential the chain
|
|
146
|
+
-- resolved.
|
|
147
|
+
self._exchange = nil
|
|
148
|
+
self._refresh = nil
|
|
149
|
+
|
|
150
|
+
-- The RESOLVED credential (nil when none), the resolved flag the cache
|
|
151
|
+
-- keeps, and the construction failure the transport gate refuses on.
|
|
152
|
+
self._cred = nil
|
|
153
|
+
self._resolved = false
|
|
154
|
+
self._initerr = nil
|
|
155
|
+
|
|
156
|
+
return self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
-- init is sync by feature contract: build the chain, never look anything
|
|
161
|
+
-- up here.
|
|
162
|
+
function SecretsFeature:init(ctx, options)
|
|
163
|
+
self.client = ctx.client
|
|
164
|
+
self.options = options or {}
|
|
165
|
+
self._liveopts = ctx.options or {}
|
|
166
|
+
self.active = (self.options["active"] == true)
|
|
167
|
+
|
|
168
|
+
if not self.active then
|
|
169
|
+
return
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
self._secretname = optstr(self.options, "name", "apikey")
|
|
173
|
+
self._cache = (self.options["cache"] ~= false)
|
|
174
|
+
|
|
175
|
+
-- Exchange config, normalised once. Nil when off, so every later
|
|
176
|
+
-- decision is a nil check.
|
|
177
|
+
local xopts = helpers.to_map(self.options["exchange"])
|
|
178
|
+
if xopts ~= nil and xopts["active"] == true then
|
|
179
|
+
local statuses = {}
|
|
180
|
+
if type(xopts["statuses"]) == "table" then
|
|
181
|
+
for _, s in ipairs(xopts["statuses"]) do
|
|
182
|
+
statuses[#statuses + 1] = math.floor(tonumber(s) or 0)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
if #statuses == 0 then
|
|
186
|
+
statuses = { 401 }
|
|
187
|
+
end
|
|
188
|
+
self._exchange = {
|
|
189
|
+
path = optstr(xopts, "path", "auth/token"),
|
|
190
|
+
method = optstr(xopts, "method", "POST"),
|
|
191
|
+
request = optstr(xopts, "request", "refresh_token"),
|
|
192
|
+
response = optstr(xopts, "response", "access_token"),
|
|
193
|
+
statuses = statuses,
|
|
194
|
+
retries = optnum(xopts, "retries", 1),
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
-- WRAP FIRST, before anything below can fail. The gate lives in the
|
|
199
|
+
-- wrapper, so a chain that cannot be built must still find it installed
|
|
200
|
+
-- - otherwise a construction failure would leave the ORIGINAL transport
|
|
201
|
+
-- in place and every request would go out unauthenticated, which is the
|
|
202
|
+
-- one outcome this feature exists to prevent. Wrapping whatever
|
|
203
|
+
-- transport is current at init also means the exchange (when on) SEES
|
|
204
|
+
-- responses, which is the only place expiry is ever discovered.
|
|
205
|
+
local secrets_self = self
|
|
206
|
+
local utility = ctx.utility
|
|
207
|
+
local inner = utility.fetcher
|
|
208
|
+
utility.fetcher = function(fctx, fullurl, fetchdef)
|
|
209
|
+
return secrets_self:_transport(fctx, fullurl, fetchdef, inner)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
-- The explicit credential, when set, is the first store in the chain.
|
|
213
|
+
--
|
|
214
|
+
-- WHICH option that is depends on the exchange. Without one, the secret
|
|
215
|
+
-- being resolved IS the credential the transport sends, so `apikey` is
|
|
216
|
+
-- it. With one, the secret is a REFRESH token and `apikey` means the
|
|
217
|
+
-- opposite thing - an access token the caller already holds - so the
|
|
218
|
+
-- explicit seat belongs to `exchange.refresh`, and apikey is left alone
|
|
219
|
+
-- to serve as the starting access token (see _resolve_once).
|
|
220
|
+
local explicit = nil
|
|
221
|
+
if self._exchange == nil then
|
|
222
|
+
explicit = self._liveopts["apikey"]
|
|
223
|
+
elseif xopts ~= nil then
|
|
224
|
+
explicit = xopts["refresh"]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
local specs = {}
|
|
228
|
+
|
|
229
|
+
if type(explicit) == "string" and explicit ~= "" then
|
|
230
|
+
local ok, key = pcall(sekreto.envkey, self._secretname, "")
|
|
231
|
+
if ok then
|
|
232
|
+
specs[#specs + 1] = {
|
|
233
|
+
kind = "memory",
|
|
234
|
+
name = "options",
|
|
235
|
+
values = { [key] = explicit },
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
-- An invalid secret `name` is a misconfiguration, and sekreto's own
|
|
239
|
+
-- message says which. Refused at the gate rather than skipped.
|
|
240
|
+
self._initerr = key
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
-- The chain, entry by entry. A table is handed to sekreto verbatim - a
|
|
245
|
+
-- live provider (callable `lookup`, duck-typed) or a spec, and a spec
|
|
246
|
+
-- naming a kind this SDK was not generated with is refused by sekreto's
|
|
247
|
+
-- constructor with its own message. Anything else - a bare kind name
|
|
248
|
+
-- ("hashicorp"), a number, a nil hole - is refused HERE (see
|
|
249
|
+
-- NOTAPROVIDER); the list is walked to its highest integer key so a hole
|
|
250
|
+
-- cannot end it early. The first refusal is the one reported.
|
|
251
|
+
local given = self.options["providers"]
|
|
252
|
+
if given ~= nil then
|
|
253
|
+
if type(given) ~= "table" then
|
|
254
|
+
self._initerr = self._initerr
|
|
255
|
+
or sekreto.SekretoError(NOTAPROVIDER .. tostring(given))
|
|
256
|
+
else
|
|
257
|
+
local last = 0
|
|
258
|
+
for k in pairs(given) do
|
|
259
|
+
if math.type(k) == "integer" and k > last then
|
|
260
|
+
last = k
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
for i = 1, last do
|
|
264
|
+
local entry = given[i]
|
|
265
|
+
if type(entry) == "table" then
|
|
266
|
+
specs[#specs + 1] = entry
|
|
267
|
+
else
|
|
268
|
+
self._initerr = self._initerr
|
|
269
|
+
or sekreto.SekretoError(NOTAPROVIDER .. tostring(entry))
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
local plugs, perr = feature_plugins(self.name)
|
|
276
|
+
if perr ~= nil then
|
|
277
|
+
self._initerr = self._initerr or perr
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
-- Construction contacts nothing: a provider opens nothing until its
|
|
281
|
+
-- first lookup. A refusal here (unknown kind, a plugin kind not passed
|
|
282
|
+
-- in, a spec the kind rejects) is kept for the gate: init cannot fail
|
|
283
|
+
-- the construction the way ts's throwing init does, so the transport
|
|
284
|
+
-- refuses to send instead, which keeps a misconfigured chain
|
|
285
|
+
-- fail-closed rather than silently unauthenticated.
|
|
286
|
+
local ok, sek = pcall(sekreto.sekreto, {
|
|
287
|
+
providers = specs,
|
|
288
|
+
plugins = plugs or {},
|
|
289
|
+
cache = self._cache,
|
|
290
|
+
})
|
|
291
|
+
if not ok then
|
|
292
|
+
self._initerr = self._initerr or sek
|
|
293
|
+
return
|
|
294
|
+
end
|
|
295
|
+
self._sek = sek
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
-- The LIVE Sekreto instance, for callers who want arbitrary secrets or
|
|
300
|
+
-- redaction (the lua spelling of ts's public sekreto() accessor):
|
|
301
|
+
--
|
|
302
|
+
-- feature:sekreto():get("db.password")
|
|
303
|
+
-- feature:sekreto():redact(logline)
|
|
304
|
+
--
|
|
305
|
+
-- Never a clone: sekreto holds provider state that has to stay live to be
|
|
306
|
+
-- worth anything. Nil while the chain could not be built.
|
|
307
|
+
function SecretsFeature:sekreto()
|
|
308
|
+
return self._sek
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
-- The resolved credential (nil when none) - the state the transport
|
|
313
|
+
-- injects. Tests and callers read it here rather than from the options
|
|
314
|
+
-- map, which this feature never mutates.
|
|
315
|
+
function SecretsFeature:credential()
|
|
316
|
+
return self._cred
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
-- The construction failure the gate refuses on, or nil.
|
|
321
|
+
function SecretsFeature:init_error()
|
|
322
|
+
return self._initerr
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
-- One resolution. A settled SUCCESS is kept only when caching is on
|
|
327
|
+
-- (`cache = false` means every request asks the chain again); a FAILURE
|
|
328
|
+
-- is never kept, so a transient vault outage does not poison the client
|
|
329
|
+
-- permanently - the next operation asks the chain again.
|
|
330
|
+
function SecretsFeature:_resolve()
|
|
331
|
+
if self._initerr ~= nil then
|
|
332
|
+
return self._initerr
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
if self._resolved and self._cache then
|
|
336
|
+
return nil
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
local err = self:_resolve_once()
|
|
340
|
+
if err ~= nil then
|
|
341
|
+
self._resolved = false
|
|
342
|
+
return err
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
self._resolved = true
|
|
346
|
+
return nil
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
function SecretsFeature:_resolve_once()
|
|
351
|
+
if self._sek == nil then
|
|
352
|
+
return nil
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
-- Miss-vs-error: `tryget` answers nil for "no store has it" and RAISES
|
|
356
|
+
-- for "a store could not answer" - only the miss falls through.
|
|
357
|
+
local ok, found = pcall(self._sek.tryget, self._sek, self._secretname)
|
|
358
|
+
if not ok then
|
|
359
|
+
return found
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
if self._exchange == nil then
|
|
363
|
+
-- A hit is the credential. An UNCACHED miss after an earlier hit is a
|
|
364
|
+
-- revocation: the chain now says no provider has the secret, so the
|
|
365
|
+
-- resolved value must not keep going out on the wire - nil RETRACTS
|
|
366
|
+
-- it. (An explicit apikey OPTION is never lost here: it seats FIRST
|
|
367
|
+
-- in the chain as a memory provider, so the chain HITS while one is
|
|
368
|
+
-- set and the miss branch is unreachable.)
|
|
369
|
+
self._cred = found
|
|
370
|
+
return nil
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
-- Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
374
|
+
-- every later purchase. A miss is not fatal here - an explicit `apikey`
|
|
375
|
+
-- may already hold a usable access token, and the API is what gets to
|
|
376
|
+
-- say whether it does.
|
|
377
|
+
self._refresh = found
|
|
378
|
+
|
|
379
|
+
local apikey = self._cred
|
|
380
|
+
if type(apikey) ~= "string" or apikey == "" then
|
|
381
|
+
-- A starting access token supplied as the OPTION, read from the live
|
|
382
|
+
-- options (no feature ever writes it).
|
|
383
|
+
apikey = self._liveopts["apikey"]
|
|
384
|
+
if type(apikey) == "string" and apikey ~= "" then
|
|
385
|
+
self._cred = apikey
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
if type(apikey) == "string" and apikey ~= "" then
|
|
389
|
+
-- A starting access token was supplied. Spend it: if it is stale the
|
|
390
|
+
-- API answers with an expiry status and the transport wrapper buys
|
|
391
|
+
-- another, which is the same path expiry takes anyway.
|
|
392
|
+
return nil
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
local _, err = self:_buy()
|
|
396
|
+
return err
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
-- The transport wrapper: whatever transport was current at init, behind
|
|
401
|
+
-- the fail-closed gate.
|
|
402
|
+
function SecretsFeature:_transport(ctx, fullurl, fetchdef, inner)
|
|
403
|
+
-- Fail-closed, at the ONE seam every wire path crosses. A construction
|
|
404
|
+
-- failure or a provider ERROR refuses the request with sekreto's own
|
|
405
|
+
-- message - never an unauthenticated send. resolve() is cached: a
|
|
406
|
+
-- settled success is free, and with `cache = false` the chain is asked
|
|
407
|
+
-- once per REQUEST, which is that option's meaning.
|
|
408
|
+
local refused = self:_resolve()
|
|
409
|
+
if refused ~= nil then
|
|
410
|
+
return nil, ctx:make_error("secrets_refused", sekreto.errmessage(refused))
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
-- Inject the resolved credential into THIS request's header. The header
|
|
414
|
+
-- was built by prepare_auth from the options apikey; the chain-resolved
|
|
415
|
+
-- value lives in feature state instead (see _cred), so the wrapper
|
|
416
|
+
-- writes it here - same construction, same suppression rule - and the
|
|
417
|
+
-- shared options map stays untouched.
|
|
418
|
+
local cred = self._cred
|
|
419
|
+
if type(cred) == "string" and cred ~= "" then
|
|
420
|
+
self:_reauth(fetchdef, cred)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
if self._exchange == nil then
|
|
424
|
+
return inner(ctx, fullurl, fetchdef)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
return self:_with_refresh(ctx, fullurl, fetchdef, inner)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
-- Buy a token and try the request again when the API says the current one
|
|
432
|
+
-- is spent. The retry rewrites the authorization header IN PLACE on the
|
|
433
|
+
-- fetchdef, because the header was built before this request left and it
|
|
434
|
+
-- carries the token that just failed.
|
|
435
|
+
function SecretsFeature:_with_refresh(ctx, fullurl, fetchdef, inner)
|
|
436
|
+
-- A suppressed `auth` is the documented way to send NO credential, and
|
|
437
|
+
-- prepare_auth honours it by removing the header. A refusal of a
|
|
438
|
+
-- deliberately unauthenticated request is not an expired token and
|
|
439
|
+
-- cannot be fixed by buying one - retrying would transmit exactly the
|
|
440
|
+
-- credential the caller suppressed.
|
|
441
|
+
if self._liveopts["auth"] == nil then
|
|
442
|
+
return inner(ctx, fullurl, fetchdef)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
local max = self._exchange.retries
|
|
446
|
+
local attempt = 0
|
|
447
|
+
|
|
448
|
+
while true do
|
|
449
|
+
-- The credential THIS attempt goes out with, captured before it
|
|
450
|
+
-- leaves: it is what tells a stale refusal apart from a fresh one.
|
|
451
|
+
local used = self._cred
|
|
452
|
+
|
|
453
|
+
local res, err = inner(ctx, fullurl, fetchdef)
|
|
454
|
+
|
|
455
|
+
if err ~= nil or attempt >= max or not self:_spent(res) then
|
|
456
|
+
return res, err
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
-- Another request may have bought a token while this one was in
|
|
460
|
+
-- flight (a coroutine-driven caller): spend what is current before
|
|
461
|
+
-- buying, because a second exchange for a token that is already fresh
|
|
462
|
+
-- is wasted, and on a provider that invalidates the previous
|
|
463
|
+
-- credential on issuance it breaks the first request's own retry.
|
|
464
|
+
local current = self._cred
|
|
465
|
+
local token = nil
|
|
466
|
+
|
|
467
|
+
if type(current) == "string" and current ~= "" and current ~= used then
|
|
468
|
+
token = current
|
|
469
|
+
else
|
|
470
|
+
local bought, berr = self:_buy()
|
|
471
|
+
if berr ~= nil then
|
|
472
|
+
-- The purchase failed: answer with the API's own refusal rather
|
|
473
|
+
-- than this one. The caller asked for data, and the refusal is
|
|
474
|
+
-- the more useful of the two - the exchange error is a symptom.
|
|
475
|
+
return res, nil
|
|
476
|
+
end
|
|
477
|
+
token = bought
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
self:_reauth(fetchdef, token)
|
|
481
|
+
|
|
482
|
+
attempt = attempt + 1
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
function SecretsFeature:_spent(res)
|
|
488
|
+
if type(res) ~= "table" then
|
|
489
|
+
return false
|
|
490
|
+
end
|
|
491
|
+
local status = helpers.to_int(vs.getprop(res, "status"))
|
|
492
|
+
for _, s in ipairs(self._exchange.statuses) do
|
|
493
|
+
if s == status then
|
|
494
|
+
return true
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
return false
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
-- Write the credential into the request the way prepare_auth writes it.
|
|
502
|
+
function SecretsFeature:_reauth(fetchdef, token)
|
|
503
|
+
local headers = helpers.to_map(type(fetchdef) == "table" and fetchdef["headers"] or nil)
|
|
504
|
+
if headers == nil then
|
|
505
|
+
return
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
-- Suppressed auth means NO header, the same answer prepare_auth gives.
|
|
509
|
+
-- Lua cannot spell `auth: null` in the options a caller passes (a table
|
|
510
|
+
-- stores no nil, and validate then fills the default), so the only
|
|
511
|
+
-- expressible suppression is clearing `client.options.auth` after
|
|
512
|
+
-- construction; this is the live table, so it is honoured here too.
|
|
513
|
+
local auth = self._liveopts["auth"]
|
|
514
|
+
if type(auth) ~= "table" then
|
|
515
|
+
headers["authorization"] = nil
|
|
516
|
+
return
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
local prefix = auth["prefix"]
|
|
520
|
+
if type(prefix) ~= "string" then
|
|
521
|
+
prefix = ""
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
if prefix == "" then
|
|
525
|
+
headers["authorization"] = token
|
|
526
|
+
else
|
|
527
|
+
headers["authorization"] = prefix .. " " .. token
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
-- Buy an access token with the refresh token.
|
|
533
|
+
function SecretsFeature:_buy()
|
|
534
|
+
-- TEST MODE BUYS NOTHING. The test feature replaces the transport so no
|
|
535
|
+
-- request leaves the process; an exchange here would be the one HTTP
|
|
536
|
+
-- call it could not stop, and it would need a live token endpoint for a
|
|
537
|
+
-- suite whose whole point is not needing one. A deterministic,
|
|
538
|
+
-- obviously-fake token instead - the same answer make_options gives a
|
|
539
|
+
-- required server variable, for the same reason.
|
|
540
|
+
if self.client.mode ~= "live" then
|
|
541
|
+
local token = "test-" .. self._exchange.response
|
|
542
|
+
self._cred = token
|
|
543
|
+
return token, nil
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
local token, err = self:_buy_once()
|
|
547
|
+
if err ~= nil then
|
|
548
|
+
return nil, err
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
self._cred = token
|
|
552
|
+
return token, nil
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
-- The token-exchange transport of last resort: LuaSocket's http, answering
|
|
557
|
+
-- the same shape the system.fetch seam promises (status + json). It exists
|
|
558
|
+
-- so an exchange works with ordinary SDK options - requiring a custom
|
|
559
|
+
-- transport for the COMMON case would reject every live token purchase
|
|
560
|
+
-- before a request was made. Mirrors utility/fetcher.lua's own default
|
|
561
|
+
-- (which is a local there, not exported); like it, this is HTTP through
|
|
562
|
+
-- socket.http, and an https token endpoint needs luasec's ssl.https on the
|
|
563
|
+
-- package path, which the rockspec does not require.
|
|
564
|
+
local function raw_exchange_fetch(fullurl, fetchdef)
|
|
565
|
+
local http_ok, http = pcall(require, "socket.http")
|
|
566
|
+
local ltn12_ok, ltn12 = pcall(require, "ltn12")
|
|
567
|
+
if not http_ok or not ltn12_ok then
|
|
568
|
+
return nil, "HTTP library not available (install luasocket)"
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
local headers = {}
|
|
572
|
+
for k, v in pairs(fetchdef["headers"] or {}) do
|
|
573
|
+
if type(v) == "string" then
|
|
574
|
+
headers[k] = v
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
local body = fetchdef["body"]
|
|
579
|
+
if type(body) ~= "string" then
|
|
580
|
+
body = nil
|
|
581
|
+
end
|
|
582
|
+
if body ~= nil then
|
|
583
|
+
headers["content-length"] = tostring(#body)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
local chunks = {}
|
|
587
|
+
local res, code = http.request({
|
|
588
|
+
url = fullurl,
|
|
589
|
+
method = fetchdef["method"] or "POST",
|
|
590
|
+
headers = headers,
|
|
591
|
+
source = body and ltn12.source.string(body) or nil,
|
|
592
|
+
sink = ltn12.sink.table(chunks),
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
if not res then
|
|
596
|
+
return nil, code
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
local raw = table.concat(chunks)
|
|
600
|
+
|
|
601
|
+
return {
|
|
602
|
+
status = code,
|
|
603
|
+
json = function()
|
|
604
|
+
if #raw == 0 then
|
|
605
|
+
return nil
|
|
606
|
+
end
|
|
607
|
+
local decoded = json.decode(raw)
|
|
608
|
+
return decoded
|
|
609
|
+
end,
|
|
610
|
+
body = raw,
|
|
611
|
+
}, nil
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
function SecretsFeature:_buy_once()
|
|
616
|
+
local x = self._exchange
|
|
617
|
+
|
|
618
|
+
if type(self._refresh) ~= "string" or self._refresh == "" then
|
|
619
|
+
return nil, sekreto.SekretoError(
|
|
620
|
+
"secrets: no refresh token: the provider chain has no '" ..
|
|
621
|
+
self._secretname .. "', and feature.secrets.exchange.refresh is unset")
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
local options = self.client:options_map()
|
|
625
|
+
|
|
626
|
+
-- The token endpoint is RELATIVE to the base, which already carries
|
|
627
|
+
-- whatever account or tenant segment the server URL declares.
|
|
628
|
+
local base = vs.getprop(options, "base")
|
|
629
|
+
if type(base) ~= "string" then
|
|
630
|
+
base = ""
|
|
631
|
+
end
|
|
632
|
+
base = base:gsub("/+$", "")
|
|
633
|
+
local url = base .. "/" .. (x.path:gsub("^/+", ""))
|
|
634
|
+
|
|
635
|
+
-- Deliberately NOT the SDK transport. The transport is what this feature
|
|
636
|
+
-- wraps, and sending the token request back through it would recurse on
|
|
637
|
+
-- the first expiry - and would route the exchange through the test mock,
|
|
638
|
+
-- which knows nothing about it. system.fetch, when the caller supplied
|
|
639
|
+
-- one, is the raw seam beneath the transport and is honoured.
|
|
640
|
+
local fetch = vs.getpath(options, "system.fetch")
|
|
641
|
+
if type(fetch) ~= "function" then
|
|
642
|
+
fetch = raw_exchange_fetch
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
-- The body is ENCODED, never concatenated: a refresh token (or a
|
|
646
|
+
-- configured request-field name) carrying a quote, backslash or newline
|
|
647
|
+
-- must arrive as that literal value, not as malformed JSON.
|
|
648
|
+
local res, err = fetch(url, {
|
|
649
|
+
method = x.method,
|
|
650
|
+
headers = { ["content-type"] = "application/json" },
|
|
651
|
+
body = json.encode({ [x.request] = self._refresh }),
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
if err ~= nil then
|
|
655
|
+
return nil, sekreto.SekretoError(
|
|
656
|
+
"secrets: token exchange failed: " .. tostring(err) .. " from " .. url)
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
local status = type(res) == "table" and helpers.to_int(vs.getprop(res, "status")) or -1
|
|
660
|
+
if status < 200 or status >= 300 then
|
|
661
|
+
return nil, sekreto.SekretoError(
|
|
662
|
+
"secrets: token exchange failed: " .. tostring(status) .. " from " .. url)
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
local body = nil
|
|
666
|
+
local jf = vs.getprop(res, "json")
|
|
667
|
+
if type(jf) == "function" then
|
|
668
|
+
local ok, decoded = pcall(jf)
|
|
669
|
+
if ok then
|
|
670
|
+
body = decoded
|
|
671
|
+
end
|
|
672
|
+
else
|
|
673
|
+
body = vs.getprop(res, "body")
|
|
674
|
+
if type(body) == "string" then
|
|
675
|
+
body = json.decode(body)
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
local token = type(body) == "table" and body[x.response] or nil
|
|
680
|
+
|
|
681
|
+
if type(token) ~= "string" or token == "" then
|
|
682
|
+
return nil, sekreto.SekretoError(
|
|
683
|
+
"secrets: token exchange returned no '" .. x.response .. "' field from " .. url)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
return token, nil
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
return SecretsFeature
|
|
File without changes
|