@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
|
@@ -574,6 +574,69 @@ class SecretsFeatureTest < Minitest::Test
|
|
|
574
574
|
"after the chain reports a miss the retracted credential must not go out; the wire saw #{last['auth'].inspect}")
|
|
575
575
|
end
|
|
576
576
|
|
|
577
|
+
# A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
578
|
+
# feature used to override from the layer above.
|
|
579
|
+
#
|
|
580
|
+
# DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
581
|
+
# holding a HIT, and keeping the settled resolution after a miss meant the
|
|
582
|
+
# chain was never asked again for the life of the client. A secret
|
|
583
|
+
# provisioned after startup (a mounted file, a vault policy granted a
|
|
584
|
+
# minute late) was invisible forever, and the only workaround was giving
|
|
585
|
+
# up hit caching entirely.
|
|
586
|
+
def test_a_cached_miss_is_reasked
|
|
587
|
+
present = false
|
|
588
|
+
calls = 0
|
|
589
|
+
wire = Wire.new
|
|
590
|
+
client = secrets_client(wire, {
|
|
591
|
+
"feature" => { "secrets" => {
|
|
592
|
+
"active" => true,
|
|
593
|
+
"providers" => [CustomProvider.new { |_name|
|
|
594
|
+
calls += 1
|
|
595
|
+
present ? "LATEKEY01" : nil
|
|
596
|
+
}],
|
|
597
|
+
} },
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
drive_op(client, wire)
|
|
601
|
+
first = wire.api[0]
|
|
602
|
+
refute(first["has"] && !first["auth"].to_s.empty?,
|
|
603
|
+
"the chain has nothing yet, so no credential should go out")
|
|
604
|
+
|
|
605
|
+
asked = calls
|
|
606
|
+
assert_operator asked, :>, 0
|
|
607
|
+
|
|
608
|
+
# The secret is provisioned while the client is live.
|
|
609
|
+
present = true
|
|
610
|
+
|
|
611
|
+
drive_op(client, wire)
|
|
612
|
+
assert_credential(wire.api[-1]["auth"], "LATEKEY01")
|
|
613
|
+
|
|
614
|
+
assert_operator calls, :>, asked,
|
|
615
|
+
"the MISS was cached: a secret that appears later can never be picked up"
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# The other half of the same rule: a HIT is still cached by default, so
|
|
619
|
+
# the fix above must not turn every request into a chain walk.
|
|
620
|
+
def test_a_cached_hit_is_kept
|
|
621
|
+
calls = 0
|
|
622
|
+
wire = Wire.new
|
|
623
|
+
client = secrets_client(wire, {
|
|
624
|
+
"feature" => { "secrets" => {
|
|
625
|
+
"active" => true,
|
|
626
|
+
"providers" => [CustomProvider.new { |_name|
|
|
627
|
+
calls += 1
|
|
628
|
+
"STABLEKEY01"
|
|
629
|
+
}],
|
|
630
|
+
} },
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
drive_op(client, wire)
|
|
634
|
+
drive_op(client, wire)
|
|
635
|
+
|
|
636
|
+
assert_equal 1, calls,
|
|
637
|
+
"a hit must be cached under the default cache: true"
|
|
638
|
+
end
|
|
639
|
+
|
|
577
640
|
def test_auth_nil_suppresses_the_credential_chain_or_no_chain
|
|
578
641
|
setenv("APIKEY", "ENVKEY03")
|
|
579
642
|
wire = Wire.new
|
|
@@ -697,6 +760,36 @@ class SecretsFeatureTest < Minitest::Test
|
|
|
697
760
|
clearenv("REFRESH_TOKEN")
|
|
698
761
|
end
|
|
699
762
|
|
|
763
|
+
# `auth: nil` SUPPRESSES THE PURCHASE, not just the retry.
|
|
764
|
+
#
|
|
765
|
+
# resolve runs before _with_refresh's suppression check, so the refresh
|
|
766
|
+
# token used to go to the token endpoint in a request body even here.
|
|
767
|
+
# Stopping the retry does not unsend it - this asserts on the token
|
|
768
|
+
# endpoint, which is the half the API-header assertions cannot see.
|
|
769
|
+
def test_auth_nil_buys_no_token_at_all
|
|
770
|
+
setenv("REFRESH_TOKEN", "REFRESH01")
|
|
771
|
+
wire = Wire.new
|
|
772
|
+
wire.apistatus = [401]
|
|
773
|
+
|
|
774
|
+
client = secrets_client(wire, {
|
|
775
|
+
"auth" => nil,
|
|
776
|
+
"feature" => secrets_opts({
|
|
777
|
+
"name" => "refresh_token",
|
|
778
|
+
"exchange" => { "active" => true },
|
|
779
|
+
}),
|
|
780
|
+
})
|
|
781
|
+
|
|
782
|
+
drive_op(client, wire)
|
|
783
|
+
|
|
784
|
+
assert_equal 0, wire.token.length,
|
|
785
|
+
"auth nil suppressed the credential but the refresh token was still " \
|
|
786
|
+
"POSTed to the exchange endpoint"
|
|
787
|
+
refute wire.api[0]["has"],
|
|
788
|
+
"no credential may be sent when auth is suppressed"
|
|
789
|
+
ensure
|
|
790
|
+
clearenv("REFRESH_TOKEN")
|
|
791
|
+
end
|
|
792
|
+
|
|
700
793
|
# A second refusal on a token bought moments ago is a real failure, not a
|
|
701
794
|
# spin: exactly one rebuy, and the refusal is returned as it stands.
|
|
702
795
|
def test_a_second_refusal_is_returned_as_is
|
|
@@ -507,9 +507,17 @@ fn secrets_call(state: &Rc<RefCell<SecretsState>>, arg: &Value) -> Value {
|
|
|
507
507
|
|
|
508
508
|
// ---- resolution ---------------------------------------------------------
|
|
509
509
|
|
|
510
|
-
// One resolution, shared by every request. A settled
|
|
511
|
-
//
|
|
512
|
-
//
|
|
510
|
+
// One resolution, shared by every request. A settled HIT is kept only when
|
|
511
|
+
// caching is on; a FAILURE is never kept, so a transient vault outage never
|
|
512
|
+
// poisons the client permanently - the next operation asks again.
|
|
513
|
+
//
|
|
514
|
+
// A MISS is not kept either, however caching is set. That rule is
|
|
515
|
+
// sekreto's, not this feature's: `A miss is never cached: the next read
|
|
516
|
+
// asks again`, in sekreto's own source. Keeping a settled miss here would
|
|
517
|
+
// override that from the layer above, and a secret provisioned after
|
|
518
|
+
// startup - a mounted file, a policy granted a minute late - would never be
|
|
519
|
+
// picked up for the life of the client. `cache` is about caching a HIT; it
|
|
520
|
+
// was never a promise to keep saying no.
|
|
513
521
|
fn resolve(state: &Rc<RefCell<SecretsState>>) -> Result<(), ProjectNameError> {
|
|
514
522
|
{
|
|
515
523
|
let s = state.borrow();
|
|
@@ -522,9 +530,9 @@ fn resolve(state: &Rc<RefCell<SecretsState>>) -> Result<(), ProjectNameError> {
|
|
|
522
530
|
}
|
|
523
531
|
|
|
524
532
|
match resolve_once(state) {
|
|
525
|
-
Ok(
|
|
533
|
+
Ok(hit) => {
|
|
526
534
|
let cache = state.borrow().cache;
|
|
527
|
-
if cache {
|
|
535
|
+
if cache && hit {
|
|
528
536
|
state.borrow_mut().resolved = true;
|
|
529
537
|
}
|
|
530
538
|
Ok(())
|
|
@@ -536,11 +544,14 @@ fn resolve(state: &Rc<RefCell<SecretsState>>) -> Result<(), ProjectNameError> {
|
|
|
536
544
|
}
|
|
537
545
|
}
|
|
538
546
|
|
|
539
|
-
|
|
547
|
+
// Resolve once, answering whether a credential came out of it. That bool is
|
|
548
|
+
// the whole of what resolve needs to tell a cacheable HIT from a miss it
|
|
549
|
+
// must not keep.
|
|
550
|
+
fn resolve_once(state: &Rc<RefCell<SecretsState>>) -> Result<bool, ProjectNameError> {
|
|
540
551
|
let name = {
|
|
541
552
|
let s = state.borrow();
|
|
542
553
|
if s.sek.is_none() {
|
|
543
|
-
return Ok(
|
|
554
|
+
return Ok(false);
|
|
544
555
|
}
|
|
545
556
|
s.secretname.clone()
|
|
546
557
|
};
|
|
@@ -568,8 +579,9 @@ fn resolve_once(state: &Rc<RefCell<SecretsState>>) -> Result<(), ProjectNameErro
|
|
|
568
579
|
// apikey OPTION is never lost here - it seats FIRST in the
|
|
569
580
|
// chain as a memory provider, so the chain HITS while one is
|
|
570
581
|
// set and the miss branch is unreachable.)
|
|
582
|
+
let hit = found.is_some();
|
|
571
583
|
s.cred = found.unwrap_or_default();
|
|
572
|
-
return Ok(
|
|
584
|
+
return Ok(hit);
|
|
573
585
|
}
|
|
574
586
|
|
|
575
587
|
// Exchanging: what the chain resolved is the REFRESH token, kept
|
|
@@ -589,11 +601,22 @@ fn resolve_once(state: &Rc<RefCell<SecretsState>>) -> Result<(), ProjectNameErro
|
|
|
589
601
|
// A starting access token was supplied. Spend it: if it is
|
|
590
602
|
// stale the API answers with an expiry status and the wrapper
|
|
591
603
|
// buys another, which is the same path expiry takes anyway.
|
|
592
|
-
return Ok(
|
|
604
|
+
return Ok(true);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
608
|
+
// purchase is a credential-bearing call: the refresh token goes to
|
|
609
|
+
// the token endpoint in the request body. with_refresh honours
|
|
610
|
+
// suppression for the RETRY, but it runs after this - by then the
|
|
611
|
+
// refresh token has already left the process, and no later check
|
|
612
|
+
// can call it back. The suppression has to be honoured here, before
|
|
613
|
+
// the first purchase, or it only ever half-held.
|
|
614
|
+
if !authactive(&s.options) {
|
|
615
|
+
return Ok(false);
|
|
593
616
|
}
|
|
594
617
|
}
|
|
595
618
|
|
|
596
|
-
buy(state).map(|_|
|
|
619
|
+
buy(state).map(|_| true)
|
|
597
620
|
}
|
|
598
621
|
|
|
599
622
|
// ---- the transport seam -------------------------------------------------
|
|
@@ -989,6 +989,90 @@ fn cache_false_asks_the_chain_on_every_request() {
|
|
|
989
989
|
credential_is(call_auth(&wire.api(), 1), "KEY2");
|
|
990
990
|
}
|
|
991
991
|
|
|
992
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this feature
|
|
993
|
+
// used to override from the layer above.
|
|
994
|
+
//
|
|
995
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
996
|
+
// holding a HIT (the test below pins that half), and keeping the settled
|
|
997
|
+
// resolution after a miss meant the chain was never asked again for the life
|
|
998
|
+
// of the client. A secret provisioned after startup (a mounted file, a vault
|
|
999
|
+
// policy granted a minute late) was invisible forever.
|
|
1000
|
+
#[test]
|
|
1001
|
+
fn cache_true_re_asks_after_a_miss() {
|
|
1002
|
+
let wire = Wire::new();
|
|
1003
|
+
let calls = Rc::new(RefCell::new(0i64));
|
|
1004
|
+
let present = Rc::new(RefCell::new(false));
|
|
1005
|
+
let n = calls.clone();
|
|
1006
|
+
let p = present.clone();
|
|
1007
|
+
|
|
1008
|
+
let client = sdk(
|
|
1009
|
+
&wire,
|
|
1010
|
+
vec![providers(vec![callable(move |_name| {
|
|
1011
|
+
*n.borrow_mut() += 1;
|
|
1012
|
+
if *p.borrow() {
|
|
1013
|
+
Value::str("LATEKEY01")
|
|
1014
|
+
} else {
|
|
1015
|
+
Value::Noval
|
|
1016
|
+
}
|
|
1017
|
+
})])],
|
|
1018
|
+
);
|
|
1019
|
+
|
|
1020
|
+
client
|
|
1021
|
+
.direct(jo(vec![("path", Value::str("/one"))]))
|
|
1022
|
+
.expect("direct");
|
|
1023
|
+
assert_eq!(
|
|
1024
|
+
call_auth(&wire.api(), 0),
|
|
1025
|
+
None,
|
|
1026
|
+
"the chain has nothing yet, so no credential should go out: {}",
|
|
1027
|
+
wire.trace()
|
|
1028
|
+
);
|
|
1029
|
+
|
|
1030
|
+
let asked = *calls.borrow();
|
|
1031
|
+
assert!(0 < asked, "the chain was never asked");
|
|
1032
|
+
|
|
1033
|
+
// The secret is provisioned while the client is live.
|
|
1034
|
+
*present.borrow_mut() = true;
|
|
1035
|
+
|
|
1036
|
+
client
|
|
1037
|
+
.direct(jo(vec![("path", Value::str("/two"))]))
|
|
1038
|
+
.expect("direct");
|
|
1039
|
+
|
|
1040
|
+
assert!(
|
|
1041
|
+
asked < *calls.borrow(),
|
|
1042
|
+
"the MISS was cached: a secret that appears later can never be picked up"
|
|
1043
|
+
);
|
|
1044
|
+
credential_is(call_auth(&wire.api(), 1), "LATEKEY01");
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// The other half of the same rule: a HIT is still cached by default, so the
|
|
1048
|
+
// fix above must not turn every request into a chain walk.
|
|
1049
|
+
#[test]
|
|
1050
|
+
fn cache_true_keeps_a_hit() {
|
|
1051
|
+
let wire = Wire::new();
|
|
1052
|
+
let calls = Rc::new(RefCell::new(0i64));
|
|
1053
|
+
let n = calls.clone();
|
|
1054
|
+
|
|
1055
|
+
let client = sdk(
|
|
1056
|
+
&wire,
|
|
1057
|
+
vec![providers(vec![callable(move |_name| {
|
|
1058
|
+
*n.borrow_mut() += 1;
|
|
1059
|
+
Value::str("STABLEKEY01")
|
|
1060
|
+
})])],
|
|
1061
|
+
);
|
|
1062
|
+
|
|
1063
|
+
for _ in 0..2 {
|
|
1064
|
+
client
|
|
1065
|
+
.direct(jo(vec![("path", Value::str("/thing"))]))
|
|
1066
|
+
.expect("direct");
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
assert_eq!(
|
|
1070
|
+
*calls.borrow(),
|
|
1071
|
+
1,
|
|
1072
|
+
"a hit must be cached under the default cache: true"
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
992
1076
|
// An UNCACHED miss after a hit is a revocation: the resolved value must
|
|
993
1077
|
// stop going out.
|
|
994
1078
|
#[test]
|
|
@@ -1403,6 +1487,18 @@ fn exchange_auth_null_suppresses_and_is_never_retried() {
|
|
|
1403
1487
|
"no credential may be sent when auth is suppressed: {}",
|
|
1404
1488
|
wire.trace()
|
|
1405
1489
|
);
|
|
1490
|
+
|
|
1491
|
+
// AND NO PURCHASE. resolve() runs before with_refresh's suppression
|
|
1492
|
+
// check, so the refresh token used to go to the token endpoint in a
|
|
1493
|
+
// request body even here. Stopping the retry does not unsend it, and
|
|
1494
|
+
// only the token endpoint can see this.
|
|
1495
|
+
assert_eq!(
|
|
1496
|
+
wire.token().len(),
|
|
1497
|
+
0,
|
|
1498
|
+
"auth null suppressed the credential but the refresh token was still \
|
|
1499
|
+
POSTed to the exchange endpoint: {}",
|
|
1500
|
+
wire.trace()
|
|
1501
|
+
);
|
|
1406
1502
|
}
|
|
1407
1503
|
|
|
1408
1504
|
// The exchange with NO system.fetch: the ordinary case, where make_options
|
|
@@ -264,18 +264,29 @@ class SecretsFeature extends BaseFeature("secrets", "0.1.0", true) {
|
|
|
264
264
|
else withrefresh(ctx, url, fetchdef, inner)
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
// One resolution, shared by every concurrent caller. A settled
|
|
267
|
+
// One resolution, shared by every concurrent caller. A settled HIT is
|
|
268
268
|
// kept only when caching is on; a FAILURE is never kept, so the next
|
|
269
269
|
// operation asks the chain again.
|
|
270
|
+
//
|
|
271
|
+
// A MISS is not kept either, however caching is set. That rule is
|
|
272
|
+
// sekreto's, not this feature's: `A miss is never cached: the next read
|
|
273
|
+
// asks again`, in sekreto's own source. Keeping a settled miss here would
|
|
274
|
+
// override that from the layer above, and a secret provisioned after
|
|
275
|
+
// startup - a mounted file, a policy granted a minute late - would never
|
|
276
|
+
// be picked up for the life of the client. `cache` is about caching a
|
|
277
|
+
// HIT; it was never a promise to keep saying no.
|
|
270
278
|
private def resolve(): Unit = lock.synchronized {
|
|
271
279
|
if (!this.settled) {
|
|
272
|
-
resolveonce()
|
|
273
|
-
if (this.docache) this.settled = true
|
|
280
|
+
val hit = resolveonce()
|
|
281
|
+
if (this.docache && hit) this.settled = true
|
|
274
282
|
}
|
|
275
283
|
}
|
|
276
284
|
|
|
277
|
-
|
|
278
|
-
|
|
285
|
+
// Resolve once, reporting whether a credential came out of it. That
|
|
286
|
+
// boolean is the whole of what resolve() needs to tell a cacheable HIT
|
|
287
|
+
// from a miss it must not keep.
|
|
288
|
+
private def resolveonce(): Boolean = {
|
|
289
|
+
if (this.sek == null) return false
|
|
279
290
|
|
|
280
291
|
// tryget returns None for a MISS and RAISES for an ERROR. That is the
|
|
281
292
|
// whole miss-vs-error rule, and it is sekreto's, not this feature's.
|
|
@@ -288,7 +299,7 @@ class SecretsFeature extends BaseFeature("secrets", "0.1.0", true) {
|
|
|
288
299
|
// here - it seats FIRST in the chain as a memory provider, so the
|
|
289
300
|
// chain HITS while one is set and this branch is unreachable.)
|
|
290
301
|
this.cred = found.getOrElse("")
|
|
291
|
-
return
|
|
302
|
+
return found.isDefined
|
|
292
303
|
}
|
|
293
304
|
|
|
294
305
|
// Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -302,9 +313,20 @@ class SecretsFeature extends BaseFeature("secrets", "0.1.0", true) {
|
|
|
302
313
|
// A starting access token was supplied. Spend it: if it is stale the API
|
|
303
314
|
// answers with an expiry status and the wrapper buys another, which is
|
|
304
315
|
// the same path expiry takes anyway.
|
|
305
|
-
if ("" != this.cred) return
|
|
316
|
+
if ("" != this.cred) return true
|
|
317
|
+
|
|
318
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
319
|
+
// purchase is a credential-bearing call: the refresh token goes to the
|
|
320
|
+
// token endpoint in the request body. withrefresh honours suppression
|
|
321
|
+
// for the RETRY, but it runs after this - by then the refresh token has
|
|
322
|
+
// already left the process, and no later check can call it back. The
|
|
323
|
+
// suppression has to be honoured here, before the first purchase, or it
|
|
324
|
+
// only ever half-held.
|
|
325
|
+
if (this.liveopts == null || this.liveopts.get("auth") == null) return false
|
|
306
326
|
|
|
307
327
|
buyonce()
|
|
328
|
+
|
|
329
|
+
true
|
|
308
330
|
}
|
|
309
331
|
|
|
310
332
|
// Buy a token and try the request again when the API says the current one
|
|
@@ -612,6 +612,69 @@ object SecretsTestMain {
|
|
|
612
612
|
"out; the wire saw \"" + w.authof(last) + "\"")
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
616
|
+
// feature used to override from the layer above.
|
|
617
|
+
//
|
|
618
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
619
|
+
// holding a HIT, and keeping the settled resolution after a miss meant the
|
|
620
|
+
// chain was never asked again for the life of the client. A secret
|
|
621
|
+
// provisioned after startup (a mounted file, a vault policy granted a
|
|
622
|
+
// minute late) was invisible forever, and the only workaround was giving
|
|
623
|
+
// up hit caching entirely.
|
|
624
|
+
private def testCachedMissIsReasked(): Unit = {
|
|
625
|
+
var present = false
|
|
626
|
+
var calls = 0
|
|
627
|
+
val prov = new CodeProvider(_ => {
|
|
628
|
+
calls += 1
|
|
629
|
+
if (present) Some("LATEKEY01") else None
|
|
630
|
+
}, "late")
|
|
631
|
+
|
|
632
|
+
val w = new Wire()
|
|
633
|
+
val client = secretsClient(w, "feature" -> secretsOpts(jlist(prov)))
|
|
634
|
+
|
|
635
|
+
check("cachedmiss.drive", driveOneCall(client, w),
|
|
636
|
+
"no entity op reached the transport")
|
|
637
|
+
if (w.api().nonEmpty) {
|
|
638
|
+
val first = w.api().head
|
|
639
|
+
check("cachedmiss.nocred", !w.hasauth(first) || "" == w.authof(first),
|
|
640
|
+
"the chain has nothing yet, so no credential should go out")
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
val asked = calls
|
|
644
|
+
check("cachedmiss.asked", 0 < asked, "the chain was never asked")
|
|
645
|
+
|
|
646
|
+
// The secret is provisioned while the client is live.
|
|
647
|
+
present = true
|
|
648
|
+
check("cachedmiss.drive2", driveOneCall(client, w),
|
|
649
|
+
"the second op never reached the transport")
|
|
650
|
+
|
|
651
|
+
check("cachedmiss.reasked", asked < calls,
|
|
652
|
+
"the MISS was cached: a secret that appears later can never be picked up")
|
|
653
|
+
if (w.api().nonEmpty) {
|
|
654
|
+
credentialIs("cachedmiss.late", w.authof(w.api().last), "LATEKEY01")
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// The other half of the same rule: a HIT is still cached by default, so
|
|
659
|
+
// the fix above must not turn every request into a chain walk.
|
|
660
|
+
private def testCachedHitIsKept(): Unit = {
|
|
661
|
+
var calls = 0
|
|
662
|
+
val prov = new CodeProvider(_ => {
|
|
663
|
+
calls += 1
|
|
664
|
+
Some("STABLEKEY01")
|
|
665
|
+
}, "counting")
|
|
666
|
+
|
|
667
|
+
val w = new Wire()
|
|
668
|
+
val client = secretsClient(w, "feature" -> secretsOpts(jlist(prov)))
|
|
669
|
+
|
|
670
|
+
check("cachedhit.drive", driveOneCall(client, w),
|
|
671
|
+
"no entity op reached the transport")
|
|
672
|
+
check("cachedhit.drive2", driveOneCall(client, w),
|
|
673
|
+
"the second op never reached the transport")
|
|
674
|
+
|
|
675
|
+
eqi("cachedhit.once", 1, calls)
|
|
676
|
+
}
|
|
677
|
+
|
|
615
678
|
private def testSecretNameConfigurable(): Unit = {
|
|
616
679
|
val w = new Wire()
|
|
617
680
|
val client = secretsClient(w,
|
|
@@ -899,6 +962,12 @@ object SecretsTestMain {
|
|
|
899
962
|
"no credential may be sent when auth is suppressed, got \"" +
|
|
900
963
|
w.authof(w.api().head) + "\"")
|
|
901
964
|
}
|
|
965
|
+
|
|
966
|
+
// AND NO PURCHASE. resolve() runs before withrefresh's suppression
|
|
967
|
+
// check, so the refresh token used to go to the token endpoint in a
|
|
968
|
+
// request body even here. Stopping the retry does not unsend it, and
|
|
969
|
+
// only the token endpoint can see this.
|
|
970
|
+
eqi("exchange.authnull.nobuy", 0, w.token().length)
|
|
902
971
|
}
|
|
903
972
|
|
|
904
973
|
// No refresh token anywhere is an ERROR, not an unauthenticated call.
|
|
@@ -945,6 +1014,8 @@ object SecretsTestMain {
|
|
|
945
1014
|
"failClosedUnbuildableChain" -> testFailClosedUnbuildableChain,
|
|
946
1015
|
"transientRecovery" -> testTransientRecovery,
|
|
947
1016
|
"uncachedMissRetracts" -> testUncachedMissRetracts,
|
|
1017
|
+
"cachedMissIsReasked" -> testCachedMissIsReasked,
|
|
1018
|
+
"cachedHitIsKept" -> testCachedHitIsKept,
|
|
948
1019
|
"secretNameConfigurable" -> testSecretNameConfigurable,
|
|
949
1020
|
"sekretoIsLive" -> testSekretoIsLive,
|
|
950
1021
|
"pluginVocabulary" -> testPluginVocabulary,
|
|
@@ -167,18 +167,25 @@ class SecretsFeature extends BaseFeature {
|
|
|
167
167
|
// the op — sekreto's miss-vs-error rule: never fall through to an
|
|
168
168
|
// unauthenticated request because a store was broken.
|
|
169
169
|
//
|
|
170
|
-
// The promise is cleared on REJECTION,
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
// be asked a second time.
|
|
170
|
+
// The promise is cleared on REJECTION, after success when caching is off,
|
|
171
|
+
// and after a MISS however caching is set. Holding a settled promise
|
|
172
|
+
// forever would mean a transient vault outage poisoned the client
|
|
173
|
+
// permanently — every later operation failing with the original error long
|
|
174
|
+
// after the vault recovered — and it would make the documented
|
|
175
|
+
// `cache: false` a lie, since the chain would never be asked a second time.
|
|
176
|
+
//
|
|
177
|
+
// The MISS rule is sekreto's, not this feature's: `A miss is never cached:
|
|
178
|
+
// the next read asks again`, in sekreto's own source. Retaining a settled
|
|
179
|
+
// miss here would override that from the layer above, and a secret
|
|
180
|
+
// provisioned after startup — a mounted file, a policy granted a minute
|
|
181
|
+
// late — would never be picked up for the life of the client. `cache` is
|
|
182
|
+
// about caching a HIT; it was never a promise to keep saying no.
|
|
176
183
|
resolve(): Promise<void> {
|
|
177
184
|
if (null == this._resolving) {
|
|
178
185
|
const inflight = this._resolveonce()
|
|
179
186
|
.then(
|
|
180
|
-
() => {
|
|
181
|
-
if (!this._cache) {
|
|
187
|
+
(hit: boolean) => {
|
|
188
|
+
if (!this._cache || !hit) {
|
|
182
189
|
this._resolving = undefined
|
|
183
190
|
}
|
|
184
191
|
},
|
|
@@ -194,9 +201,12 @@ class SecretsFeature extends BaseFeature {
|
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
|
|
197
|
-
|
|
204
|
+
// Resolve once, reporting whether a credential came out of it. That
|
|
205
|
+
// boolean is the whole of what resolve() needs to tell a cacheable HIT
|
|
206
|
+
// from a miss it must not retain.
|
|
207
|
+
private async _resolveonce(): Promise<boolean> {
|
|
198
208
|
if (null == this._sekreto) {
|
|
199
|
-
return
|
|
209
|
+
return false
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
const found = await this._sekreto.try(this._secretname)
|
|
@@ -208,7 +218,7 @@ class SecretsFeature extends BaseFeature {
|
|
|
208
218
|
// resolved value lands where the sync auth path already looks.
|
|
209
219
|
this._client._options.apikey = found
|
|
210
220
|
}
|
|
211
|
-
return
|
|
221
|
+
return undefined !== found
|
|
212
222
|
}
|
|
213
223
|
|
|
214
224
|
// Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -222,10 +232,23 @@ class SecretsFeature extends BaseFeature {
|
|
|
222
232
|
// A starting access token was supplied. Spend it: if it is stale the
|
|
223
233
|
// API answers 401 and the transport wrapper buys another, which is
|
|
224
234
|
// the same path expiry takes anyway.
|
|
225
|
-
return
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
239
|
+
// purchase is a credential-bearing call: the refresh token goes to the
|
|
240
|
+
// token endpoint in the request body. _withRefresh honours suppression
|
|
241
|
+
// for the RETRY, but it runs after this — by then the refresh token has
|
|
242
|
+
// already left the process, and no later check can call it back. The
|
|
243
|
+
// suppression has to be honoured here, before the first purchase, or it
|
|
244
|
+
// only ever half-held.
|
|
245
|
+
if (null == this._client.options().auth) {
|
|
246
|
+
return false
|
|
226
247
|
}
|
|
227
248
|
|
|
228
249
|
this._client._options.apikey = await this._buy()
|
|
250
|
+
|
|
251
|
+
return true
|
|
229
252
|
}
|
|
230
253
|
|
|
231
254
|
|
|
@@ -260,6 +260,71 @@ describe('secrets', () => {
|
|
|
260
260
|
})
|
|
261
261
|
|
|
262
262
|
|
|
263
|
+
// A MISS IS NOT A CACHEABLE ANSWER — sekreto's own rule, which this
|
|
264
|
+
// feature used to override from the layer above.
|
|
265
|
+
//
|
|
266
|
+
// Default caching here, deliberately: `cache: true` is about holding a
|
|
267
|
+
// HIT, and holding the settled promise after a miss meant the chain was
|
|
268
|
+
// never asked again for the life of the client. A secret provisioned
|
|
269
|
+
// after startup (a mounted file, a vault policy granted a minute late)
|
|
270
|
+
// was invisible forever, and the only workaround was giving up hit
|
|
271
|
+
// caching entirely.
|
|
272
|
+
test('active: a MISS is re-asked, so a late secret is picked up',
|
|
273
|
+
async () => {
|
|
274
|
+
let calls = 0
|
|
275
|
+
const sdk = (SDK as any).test({}, {
|
|
276
|
+
feature: {
|
|
277
|
+
secrets: {
|
|
278
|
+
active: true,
|
|
279
|
+
providers: [{
|
|
280
|
+
lookup(_name: string): string | undefined {
|
|
281
|
+
calls++
|
|
282
|
+
// Absent on the first ask, present on the second.
|
|
283
|
+
return 1 < calls ? 'LATEKEY' : undefined
|
|
284
|
+
},
|
|
285
|
+
describe() { return 'late:test' },
|
|
286
|
+
}],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
const first = await sdk.prepare({ path: '/' })
|
|
292
|
+
assert.equal(first.headers['authorization'], undefined,
|
|
293
|
+
'the first resolve missed, so no credential should go out')
|
|
294
|
+
|
|
295
|
+
const second = await sdk.prepare({ path: '/' })
|
|
296
|
+
credentialIs(second.headers['authorization'], 'LATEKEY')
|
|
297
|
+
|
|
298
|
+
assert.ok(1 < calls,
|
|
299
|
+
'the chain was asked once and the MISS cached: a secret that ' +
|
|
300
|
+
'appears later can never be picked up')
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
// The other half of the same rule: a HIT is still cached by default, so
|
|
305
|
+
// the fix above must not turn every request into a chain walk.
|
|
306
|
+
test('active: a HIT is still cached by default', async () => {
|
|
307
|
+
let calls = 0
|
|
308
|
+
const sdk = (SDK as any).test({}, {
|
|
309
|
+
feature: {
|
|
310
|
+
secrets: {
|
|
311
|
+
active: true,
|
|
312
|
+
providers: [{
|
|
313
|
+
lookup(_name: string): string { calls++; return 'KEY' + calls },
|
|
314
|
+
describe() { return 'counting:test' },
|
|
315
|
+
}],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
await sdk.prepare({ path: '/' })
|
|
321
|
+
await sdk.prepare({ path: '/' })
|
|
322
|
+
|
|
323
|
+
assert.equal(calls, 1,
|
|
324
|
+
'a hit must be cached under the default cache: true')
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
|
|
263
328
|
test('active: secret name is configurable', async () => {
|
|
264
329
|
process.env[ENVPREFIX + 'API_TOKEN'] = 'TOKKEY01'
|
|
265
330
|
try {
|
|
@@ -679,6 +744,14 @@ describe('secrets exchange', () => {
|
|
|
679
744
|
assert.equal(stub.api().length, 1, 'a suppressed request must not be retried')
|
|
680
745
|
assert.equal(stub.api()[0].auth, undefined,
|
|
681
746
|
'no credential may be sent when auth is suppressed')
|
|
747
|
+
|
|
748
|
+
// AND NO PURCHASE. This is the half the API-call assertions cannot see:
|
|
749
|
+
// resolve() runs before _withRefresh's suppression check, so the
|
|
750
|
+
// refresh token used to go to the token endpoint in a request body
|
|
751
|
+
// even here. Stopping the retry does not unsend it.
|
|
752
|
+
assert.equal(stub.token().length, 0,
|
|
753
|
+
'auth: null suppressed the credential but the refresh token was ' +
|
|
754
|
+
'still POSTed to the exchange endpoint')
|
|
682
755
|
})
|
|
683
756
|
|
|
684
757
|
|