@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
|
@@ -321,11 +321,19 @@ public class SecretsFeature : BaseFeature
|
|
|
321
321
|
// ---------------------------------------------------------------
|
|
322
322
|
// Resolution.
|
|
323
323
|
|
|
324
|
-
// One resolution, shared by every concurrent caller. A settled
|
|
325
|
-
//
|
|
324
|
+
// One resolution, shared by every concurrent caller. A settled HIT is
|
|
325
|
+
// kept only when caching is on (`cache: false` means every resolve
|
|
326
326
|
// asks the chain again); a FAILURE is always cleared, so a transient
|
|
327
327
|
// vault outage never poisons the client permanently - the next
|
|
328
328
|
// operation asks the chain again.
|
|
329
|
+
//
|
|
330
|
+
// A MISS is cleared too, however caching is set. That rule is
|
|
331
|
+
// sekreto's, not this feature's: `A miss is never cached: the next read
|
|
332
|
+
// asks again`, in sekreto's own source. Keeping a settled miss here
|
|
333
|
+
// would override that from the layer above, and a secret provisioned
|
|
334
|
+
// after startup - a mounted file, a policy granted a minute late -
|
|
335
|
+
// would never be picked up for the life of the client. `cache` is about
|
|
336
|
+
// caching a HIT; it was never a promise to keep saying no.
|
|
329
337
|
private void Resolve()
|
|
330
338
|
{
|
|
331
339
|
if (null != _initerr)
|
|
@@ -361,9 +369,10 @@ public class SecretsFeature : BaseFeature
|
|
|
361
369
|
}
|
|
362
370
|
|
|
363
371
|
Exception? err = null;
|
|
372
|
+
var hit = false;
|
|
364
373
|
try
|
|
365
374
|
{
|
|
366
|
-
ResolveOnce();
|
|
375
|
+
hit = ResolveOnce();
|
|
367
376
|
}
|
|
368
377
|
catch (Exception ex)
|
|
369
378
|
{
|
|
@@ -373,7 +382,7 @@ public class SecretsFeature : BaseFeature
|
|
|
373
382
|
lock (_mu)
|
|
374
383
|
{
|
|
375
384
|
call.Err = err;
|
|
376
|
-
if (null != err || !_cache)
|
|
385
|
+
if (null != err || !_cache || !hit)
|
|
377
386
|
{
|
|
378
387
|
_resolving = null;
|
|
379
388
|
}
|
|
@@ -386,11 +395,14 @@ public class SecretsFeature : BaseFeature
|
|
|
386
395
|
}
|
|
387
396
|
}
|
|
388
397
|
|
|
389
|
-
|
|
398
|
+
// Resolve once, reporting whether a credential came out of it. That
|
|
399
|
+
// bool is the whole of what Resolve() needs to tell a cacheable HIT
|
|
400
|
+
// from a miss it must not keep.
|
|
401
|
+
private bool ResolveOnce()
|
|
390
402
|
{
|
|
391
403
|
if (null == _sek)
|
|
392
404
|
{
|
|
393
|
-
return;
|
|
405
|
+
return false;
|
|
394
406
|
}
|
|
395
407
|
|
|
396
408
|
// TryGet returns null on a MISS and THROWS on a provider error -
|
|
@@ -410,7 +422,7 @@ public class SecretsFeature : BaseFeature
|
|
|
410
422
|
// HITS while one is set and this branch is unreachable.)
|
|
411
423
|
_cred = found ?? "";
|
|
412
424
|
}
|
|
413
|
-
return;
|
|
425
|
+
return null != found;
|
|
414
426
|
}
|
|
415
427
|
|
|
416
428
|
// Exchanging: what the chain resolved is the REFRESH token, kept
|
|
@@ -441,10 +453,24 @@ public class SecretsFeature : BaseFeature
|
|
|
441
453
|
// stale the API answers with an expiry status and the transport
|
|
442
454
|
// wrapper buys another, which is the same path expiry takes
|
|
443
455
|
// anyway.
|
|
444
|
-
return;
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
460
|
+
// purchase is a credential-bearing call: the refresh token goes to
|
|
461
|
+
// the token endpoint in the request body. WithRefresh honours
|
|
462
|
+
// suppression for the RETRY, but it runs after this - by then the
|
|
463
|
+
// refresh token has already left the process, and no later check
|
|
464
|
+
// can call it back. The suppression has to be honoured here, before
|
|
465
|
+
// the first purchase, or it only ever half-held.
|
|
466
|
+
if (AuthSuppressed())
|
|
467
|
+
{
|
|
468
|
+
return false;
|
|
445
469
|
}
|
|
446
470
|
|
|
447
471
|
Buy();
|
|
472
|
+
|
|
473
|
+
return true;
|
|
448
474
|
}
|
|
449
475
|
|
|
450
476
|
// ---------------------------------------------------------------
|
|
@@ -652,6 +652,82 @@ public class SecretsFeatureTest
|
|
|
652
652
|
"out; the wire saw " + last.Auth);
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
656
|
+
// feature used to override from the layer above.
|
|
657
|
+
//
|
|
658
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is
|
|
659
|
+
// about holding a HIT, and keeping the settled resolution after a miss
|
|
660
|
+
// meant the chain was never asked again for the life of the client. A
|
|
661
|
+
// secret provisioned after startup (a mounted file, a vault policy
|
|
662
|
+
// granted a minute late) was invisible forever, and the only workaround
|
|
663
|
+
// was giving up hit caching entirely.
|
|
664
|
+
[Fact]
|
|
665
|
+
public void CachedMissIsReasked()
|
|
666
|
+
{
|
|
667
|
+
var present = false;
|
|
668
|
+
var calls = 0;
|
|
669
|
+
var late = new ProbeProvider
|
|
670
|
+
{
|
|
671
|
+
Fn = _ =>
|
|
672
|
+
{
|
|
673
|
+
calls++;
|
|
674
|
+
return present ? "LATEKEY01" : null;
|
|
675
|
+
},
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
var w = new Wire();
|
|
679
|
+
var client = SecretsClient(w, new Dictionary<string, object?>
|
|
680
|
+
{
|
|
681
|
+
["feature"] = ProviderOpts(late, null),
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
DriveEntityOp(client, w);
|
|
685
|
+
var first = w.Api()[0];
|
|
686
|
+
Assert.True(!first.HasAuth || "" == first.Auth,
|
|
687
|
+
"the chain has nothing yet, so no credential should go out");
|
|
688
|
+
|
|
689
|
+
var asked = calls;
|
|
690
|
+
Assert.True(0 < asked);
|
|
691
|
+
|
|
692
|
+
// The secret is provisioned while the client is live.
|
|
693
|
+
present = true;
|
|
694
|
+
|
|
695
|
+
DriveEntityOp(client, w);
|
|
696
|
+
CredentialIs(w.Api()[w.Api().Count - 1].Auth, "LATEKEY01");
|
|
697
|
+
|
|
698
|
+
Assert.True(asked < calls,
|
|
699
|
+
"the MISS was cached: a secret that appears later can never be picked up");
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// The other half of the same rule: a HIT is still cached by default, so
|
|
703
|
+
// the fix above must not turn every request into a chain walk.
|
|
704
|
+
[Fact]
|
|
705
|
+
public void CachedHitIsKept()
|
|
706
|
+
{
|
|
707
|
+
var calls = 0;
|
|
708
|
+
var counting = new ProbeProvider
|
|
709
|
+
{
|
|
710
|
+
Fn = _ =>
|
|
711
|
+
{
|
|
712
|
+
calls++;
|
|
713
|
+
return "STABLEKEY01";
|
|
714
|
+
},
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
var w = new Wire();
|
|
718
|
+
var client = SecretsClient(w, new Dictionary<string, object?>
|
|
719
|
+
{
|
|
720
|
+
["feature"] = ProviderOpts(counting, null),
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
DriveEntityOp(client, w);
|
|
724
|
+
DriveEntityOp(client, w);
|
|
725
|
+
|
|
726
|
+
Assert.True(1 == calls,
|
|
727
|
+
"a hit must be cached under the default cache: true, chain asked " +
|
|
728
|
+
calls + " times");
|
|
729
|
+
}
|
|
730
|
+
|
|
655
731
|
[Fact]
|
|
656
732
|
public void CacheFalseAsksTheChainOnEveryResolve()
|
|
657
733
|
{
|
|
@@ -1455,6 +1531,15 @@ public class SecretsFeatureTest
|
|
|
1455
1531
|
Assert.False(w.Api()[0].HasAuth,
|
|
1456
1532
|
"no credential may be sent when auth is suppressed, got " +
|
|
1457
1533
|
w.Api()[0].Auth);
|
|
1534
|
+
|
|
1535
|
+
// AND NO PURCHASE. Resolve() runs before WithRefresh's
|
|
1536
|
+
// suppression check, so the refresh token used to go to the
|
|
1537
|
+
// token endpoint in a request body even here. Stopping the
|
|
1538
|
+
// retry does not unsend it, and only the token endpoint can
|
|
1539
|
+
// see this.
|
|
1540
|
+
Assert.True(0 == w.Token().Count,
|
|
1541
|
+
"auth null suppressed the credential but the refresh token was " +
|
|
1542
|
+
"still POSTed to the exchange endpoint");
|
|
1458
1543
|
}
|
|
1459
1544
|
finally
|
|
1460
1545
|
{
|
|
@@ -361,10 +361,18 @@ defmodule ProjectName.Feature.Secrets do
|
|
|
361
361
|
|
|
362
362
|
# ---- resolution ----------------------------------------------------------
|
|
363
363
|
|
|
364
|
-
# One resolution. A settled
|
|
364
|
+
# One resolution. A settled HIT is kept only when caching is on
|
|
365
365
|
# (`cache: false` means every resolve asks the chain again); a FAILURE is
|
|
366
366
|
# never kept, so a transient vault outage cannot poison the client
|
|
367
367
|
# permanently - the next operation asks the chain again.
|
|
368
|
+
#
|
|
369
|
+
# A MISS is not kept either, however caching is set. That rule is
|
|
370
|
+
# sekreto's, not this feature's: `A miss is never cached: the next read
|
|
371
|
+
# asks again`, in sekreto's own source. Keeping a settled miss here would
|
|
372
|
+
# override that from the layer above, and a secret provisioned after
|
|
373
|
+
# startup - a mounted file, a policy granted a minute late - would never
|
|
374
|
+
# be picked up for the life of the client. `cache` is about caching a
|
|
375
|
+
# HIT; it was never a promise to keep saying no.
|
|
368
376
|
def resolve(f) do
|
|
369
377
|
initerr = S.getprop(f, "_initerr")
|
|
370
378
|
|
|
@@ -433,7 +441,8 @@ defmodule ProjectName.Feature.Secrets do
|
|
|
433
441
|
# - it seats FIRST in the chain as a memory provider, so the chain
|
|
434
442
|
# HITS while one is set and the miss branch is unreachable.)
|
|
435
443
|
S.setprop(f, "_cred", tostr(found))
|
|
436
|
-
|
|
444
|
+
# Only a HIT is kept: see resolve/1.
|
|
445
|
+
if found != nil, do: keep(f)
|
|
437
446
|
:ok
|
|
438
447
|
else
|
|
439
448
|
# Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -451,6 +460,12 @@ defmodule ProjectName.Feature.Secrets do
|
|
|
451
460
|
keep(f)
|
|
452
461
|
:ok
|
|
453
462
|
else
|
|
463
|
+
# NO SUPPRESSION CHECK HERE, and that is deliberate: elixir's
|
|
464
|
+
# transport/5 already returns before resolve/1 when `auth: nil`, so
|
|
465
|
+
# a suppressed request never reaches this purchase at all. Every
|
|
466
|
+
# other target guards the purchase itself, because their transports
|
|
467
|
+
# resolve first. One rule, one place - and here that place is the
|
|
468
|
+
# transport.
|
|
454
469
|
case buy(f, x) do
|
|
455
470
|
{:ok, _token} ->
|
|
456
471
|
keep(f)
|
|
@@ -631,6 +631,47 @@ defmodule ProjectName.SecretsTest do
|
|
|
631
631
|
auth_is(Enum.at(api(r), 1), "KEY2")
|
|
632
632
|
end
|
|
633
633
|
|
|
634
|
+
# A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
635
|
+
# feature used to override from the layer above.
|
|
636
|
+
#
|
|
637
|
+
# DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
638
|
+
# holding a HIT (the test below pins that half), and keeping the settled
|
|
639
|
+
# resolution after a miss meant the chain was never asked again for the
|
|
640
|
+
# life of the client. A secret provisioned after startup (a mounted
|
|
641
|
+
# file, a vault policy granted a minute late) was invisible forever.
|
|
642
|
+
test "active: a cached MISS is re-asked, so a late secret is picked up" do
|
|
643
|
+
n = :counters.new(1, [])
|
|
644
|
+
present = :counters.new(1, [])
|
|
645
|
+
|
|
646
|
+
late = %{
|
|
647
|
+
lookup: fn _ ->
|
|
648
|
+
:counters.add(n, 1, 1)
|
|
649
|
+
if :counters.get(present, 1) > 0, do: "LATEKEY01", else: nil
|
|
650
|
+
end,
|
|
651
|
+
describe: fn -> "late:test" end
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
r = recorder()
|
|
655
|
+
client = sdk(r, [{"feature", secrets([{"providers", providers([late])}])}])
|
|
656
|
+
|
|
657
|
+
ProjectName.direct(client, S.jm(["path", "/one"]))
|
|
658
|
+
assert Enum.at(api(r), 0).auth == nil,
|
|
659
|
+
"the chain has nothing yet, so no credential should go out"
|
|
660
|
+
|
|
661
|
+
asked = :counters.get(n, 1)
|
|
662
|
+
assert asked > 0
|
|
663
|
+
|
|
664
|
+
# The secret is provisioned while the client is live.
|
|
665
|
+
:counters.add(present, 1, 1)
|
|
666
|
+
|
|
667
|
+
ProjectName.direct(client, S.jm(["path", "/two"]))
|
|
668
|
+
|
|
669
|
+
assert :counters.get(n, 1) > asked,
|
|
670
|
+
"the MISS was cached: a secret that appears later can never be picked up"
|
|
671
|
+
|
|
672
|
+
auth_is(Enum.at(api(r), 1), "LATEKEY01")
|
|
673
|
+
end
|
|
674
|
+
|
|
634
675
|
test "active: caching on asks the chain once" do
|
|
635
676
|
n = :counters.new(1, [])
|
|
636
677
|
counting = %{
|
|
@@ -265,10 +265,18 @@ func (f *SecretsFeature) Init(ctx *core.Context, options map[string]any) {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
// resolve runs one resolution, shared by every concurrent caller. A
|
|
268
|
-
// settled
|
|
269
|
-
//
|
|
268
|
+
// settled HIT is kept only when caching is on (`cache: false` means every
|
|
269
|
+
// resolve asks the chain again); a FAILURE is always cleared, so a
|
|
270
270
|
// transient vault outage never poisons the client permanently - the next
|
|
271
271
|
// operation asks the chain again.
|
|
272
|
+
//
|
|
273
|
+
// A MISS is cleared too, however caching is set. That rule is sekreto's,
|
|
274
|
+
// not this feature's: `A miss is never cached: the next read asks again`,
|
|
275
|
+
// in sekreto's own source. Retaining a settled miss here would override
|
|
276
|
+
// that from the layer above, and a secret provisioned after startup - a
|
|
277
|
+
// mounted file, a policy granted a minute late - would never be picked up
|
|
278
|
+
// for the life of the client. `cache` is about caching a HIT; it was never
|
|
279
|
+
// a promise to keep saying no.
|
|
272
280
|
func (f *SecretsFeature) resolve() error {
|
|
273
281
|
if nil != f.initerr {
|
|
274
282
|
return f.initerr
|
|
@@ -284,11 +292,11 @@ func (f *SecretsFeature) resolve() error {
|
|
|
284
292
|
f.resolving = call
|
|
285
293
|
f.mu.Unlock()
|
|
286
294
|
|
|
287
|
-
err := f.resolveonce()
|
|
295
|
+
hit, err := f.resolveonce()
|
|
288
296
|
|
|
289
297
|
f.mu.Lock()
|
|
290
298
|
call.err = err
|
|
291
|
-
if nil != err || !f.cache {
|
|
299
|
+
if nil != err || !f.cache || !hit {
|
|
292
300
|
f.resolving = nil
|
|
293
301
|
}
|
|
294
302
|
f.mu.Unlock()
|
|
@@ -297,16 +305,19 @@ func (f *SecretsFeature) resolve() error {
|
|
|
297
305
|
return err
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
|
|
308
|
+
// resolveonce resolves once, reporting whether a credential came out of it.
|
|
309
|
+
// That bool is the whole of what resolve() needs to tell a cacheable HIT
|
|
310
|
+
// from a miss it must not retain.
|
|
311
|
+
func (f *SecretsFeature) resolveonce() (bool, error) {
|
|
301
312
|
if nil == f.sek {
|
|
302
|
-
return nil
|
|
313
|
+
return false, nil
|
|
303
314
|
}
|
|
304
315
|
|
|
305
316
|
found, has, err := f.sek.Try(f.secretname)
|
|
306
317
|
if nil != err {
|
|
307
318
|
// A provider ERROR fails the op (via the transport gate); only a
|
|
308
319
|
// MISS falls through.
|
|
309
|
-
return err
|
|
320
|
+
return false, err
|
|
310
321
|
}
|
|
311
322
|
|
|
312
323
|
if nil == f.exchange {
|
|
@@ -323,7 +334,7 @@ func (f *SecretsFeature) resolveonce() error {
|
|
|
323
334
|
f.cred = ""
|
|
324
335
|
}
|
|
325
336
|
f.mu.Unlock()
|
|
326
|
-
return nil
|
|
337
|
+
return has, nil
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
// Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -350,11 +361,22 @@ func (f *SecretsFeature) resolveonce() error {
|
|
|
350
361
|
// A starting access token was supplied. Spend it: if it is stale
|
|
351
362
|
// the API answers with an expiry status and the transport wrapper
|
|
352
363
|
// buys another, which is the same path expiry takes anyway.
|
|
353
|
-
return nil
|
|
364
|
+
return true, nil
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// `auth: nil` is the documented way to send NO credential, and a
|
|
368
|
+
// purchase is a credential-bearing call: the refresh token goes to the
|
|
369
|
+
// token endpoint in the request body. withrefresh honours suppression
|
|
370
|
+
// for the RETRY, but it runs after this - by then the refresh token has
|
|
371
|
+
// already left the process, and no later check can call it back. The
|
|
372
|
+
// suppression has to be honoured here, before the first purchase, or it
|
|
373
|
+
// only ever half-held.
|
|
374
|
+
if nil == f.liveopts["auth"] {
|
|
375
|
+
return false, nil
|
|
354
376
|
}
|
|
355
377
|
|
|
356
378
|
_, err = f.buy()
|
|
357
|
-
return err
|
|
379
|
+
return nil == err, err
|
|
358
380
|
}
|
|
359
381
|
|
|
360
382
|
// transport wraps whatever transport was current at Init.
|
|
@@ -488,6 +488,89 @@ func TestSecretsChain(t *testing.T) {
|
|
|
488
488
|
})
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
492
|
+
// feature used to override from the layer above.
|
|
493
|
+
//
|
|
494
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
495
|
+
// holding a HIT, and holding the settled resolution after a miss meant the
|
|
496
|
+
// chain was never asked again for the life of the client. A secret
|
|
497
|
+
// provisioned after startup (a mounted file, a vault policy granted a
|
|
498
|
+
// minute late) was invisible forever, and the only workaround was giving up
|
|
499
|
+
// hit caching entirely.
|
|
500
|
+
func TestSecretsCachedMissIsReasked(t *testing.T) {
|
|
501
|
+
var mu sync.Mutex
|
|
502
|
+
calls := 0
|
|
503
|
+
|
|
504
|
+
w := makewire()
|
|
505
|
+
client := secretsClient(w, map[string]any{
|
|
506
|
+
"feature": map[string]any{"secrets": map[string]any{
|
|
507
|
+
"active": true,
|
|
508
|
+
"providers": []any{
|
|
509
|
+
&customProvider{
|
|
510
|
+
lookup: func(name string) (string, bool, error) {
|
|
511
|
+
mu.Lock()
|
|
512
|
+
defer mu.Unlock()
|
|
513
|
+
calls++
|
|
514
|
+
// Absent on the first ask, present on the second.
|
|
515
|
+
if 1 < calls {
|
|
516
|
+
return "LATEKEY01", true, nil
|
|
517
|
+
}
|
|
518
|
+
return "", false, nil
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
}},
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
driveEntityOp(t, client, w)
|
|
526
|
+
if first := w.api()[0]; first.has && "" != first.auth {
|
|
527
|
+
t.Fatalf("the first resolve missed, so no credential should go out; got %q", first.auth)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
driveEntityOp(t, client, w)
|
|
531
|
+
credentialIs(t, w.api()[len(w.api())-1].auth, "LATEKEY01")
|
|
532
|
+
|
|
533
|
+
mu.Lock()
|
|
534
|
+
defer mu.Unlock()
|
|
535
|
+
if 1 >= calls {
|
|
536
|
+
t.Fatal("the chain was asked once and the MISS cached: a secret " +
|
|
537
|
+
"that appears later can never be picked up")
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// The other half of the same rule: a HIT is still cached by default, so the
|
|
542
|
+
// fix above must not turn every request into a chain walk.
|
|
543
|
+
func TestSecretsCachedHitIsKept(t *testing.T) {
|
|
544
|
+
var mu sync.Mutex
|
|
545
|
+
calls := 0
|
|
546
|
+
|
|
547
|
+
w := makewire()
|
|
548
|
+
client := secretsClient(w, map[string]any{
|
|
549
|
+
"feature": map[string]any{"secrets": map[string]any{
|
|
550
|
+
"active": true,
|
|
551
|
+
"providers": []any{
|
|
552
|
+
&customProvider{
|
|
553
|
+
lookup: func(name string) (string, bool, error) {
|
|
554
|
+
mu.Lock()
|
|
555
|
+
defer mu.Unlock()
|
|
556
|
+
calls++
|
|
557
|
+
return "STABLEKEY01", true, nil
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
}},
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
driveEntityOp(t, client, w)
|
|
565
|
+
driveEntityOp(t, client, w)
|
|
566
|
+
|
|
567
|
+
mu.Lock()
|
|
568
|
+
defer mu.Unlock()
|
|
569
|
+
if 1 != calls {
|
|
570
|
+
t.Fatalf("a hit must be cached under the default cache: true, chain asked %d times", calls)
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
491
574
|
// customProvider is a sekreto.Provider built in code (the interface is
|
|
492
575
|
// structural: Lookup + Describe).
|
|
493
576
|
type customProvider struct {
|
|
@@ -538,6 +621,39 @@ func TestSecretsExchange(t *testing.T) {
|
|
|
538
621
|
credentialIs(t, api[1].auth, "ACCESS02")
|
|
539
622
|
})
|
|
540
623
|
|
|
624
|
+
// `auth: nil` SUPPRESSES THE PURCHASE, not just the retry.
|
|
625
|
+
//
|
|
626
|
+
// resolve() runs before withrefresh's suppression check, so the refresh
|
|
627
|
+
// token used to go to the token endpoint in a request body even here.
|
|
628
|
+
// Stopping the retry does not unsend it - this asserts on the token
|
|
629
|
+
// endpoint, which is the half the API-call assertions cannot see.
|
|
630
|
+
t.Run("auth nil buys no token at all", func(t *testing.T) {
|
|
631
|
+
os.Setenv(envprefix+"REFRESH_TOKEN", "REFRESH01")
|
|
632
|
+
defer os.Unsetenv(envprefix + "REFRESH_TOKEN")
|
|
633
|
+
|
|
634
|
+
w := makewire()
|
|
635
|
+
w.apistatus = []int{401}
|
|
636
|
+
|
|
637
|
+
client := secretsClient(w, map[string]any{
|
|
638
|
+
"auth": nil,
|
|
639
|
+
"feature": secretsOpts(map[string]any{
|
|
640
|
+
"name": "refresh_token",
|
|
641
|
+
"exchange": map[string]any{"active": true},
|
|
642
|
+
}),
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
driveEntityOp(t, client, w)
|
|
646
|
+
|
|
647
|
+
if 0 != len(w.token()) {
|
|
648
|
+
t.Fatalf("auth nil suppressed the credential but the refresh "+
|
|
649
|
+
"token was still POSTed to the exchange endpoint (%d calls)",
|
|
650
|
+
len(w.token()))
|
|
651
|
+
}
|
|
652
|
+
if call := w.api()[0]; call.has {
|
|
653
|
+
t.Fatalf("no credential may be sent when auth is suppressed, got %q", call.auth)
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
|
|
541
657
|
t.Run("test mode buys nothing and needs no token endpoint", func(t *testing.T) {
|
|
542
658
|
os.Setenv(envprefix+"REFRESH_TOKEN", "REFRESH01")
|
|
543
659
|
defer os.Unsetenv(envprefix + "REFRESH_TOKEN")
|
|
@@ -325,10 +325,18 @@ public class SecretsFeature extends BaseFeature {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
// resolve runs one resolution, shared by every concurrent caller. A
|
|
328
|
-
// settled
|
|
329
|
-
//
|
|
328
|
+
// settled HIT is kept only when caching is on (`cache: false` means every
|
|
329
|
+
// resolve asks the chain again); a FAILURE is always cleared, so a
|
|
330
330
|
// transient vault outage never poisons the client permanently - the next
|
|
331
331
|
// operation asks the chain again.
|
|
332
|
+
//
|
|
333
|
+
// A MISS is cleared too, however caching is set. That rule is sekreto's,
|
|
334
|
+
// not this feature's: `A miss is never cached: the next read asks again`,
|
|
335
|
+
// in sekreto's own source. Keeping a settled miss here would override
|
|
336
|
+
// that from the layer above, and a secret provisioned after startup - a
|
|
337
|
+
// mounted file, a policy granted a minute late - would never be picked up
|
|
338
|
+
// for the life of the client. `cache` is about caching a HIT; it was
|
|
339
|
+
// never a promise to keep saying no.
|
|
332
340
|
private void resolve() {
|
|
333
341
|
SecretsCall call;
|
|
334
342
|
boolean mine = false;
|
|
@@ -353,8 +361,9 @@ public class SecretsFeature extends BaseFeature {
|
|
|
353
361
|
}
|
|
354
362
|
|
|
355
363
|
RuntimeException err = null;
|
|
364
|
+
boolean hit = false;
|
|
356
365
|
try {
|
|
357
|
-
resolveonce();
|
|
366
|
+
hit = resolveonce();
|
|
358
367
|
}
|
|
359
368
|
catch (RuntimeException e) {
|
|
360
369
|
err = e;
|
|
@@ -362,7 +371,7 @@ public class SecretsFeature extends BaseFeature {
|
|
|
362
371
|
|
|
363
372
|
synchronized (this.lock) {
|
|
364
373
|
call.err = err;
|
|
365
|
-
if (err != null || !this.cache) {
|
|
374
|
+
if (err != null || !this.cache || !hit) {
|
|
366
375
|
this.resolving = null;
|
|
367
376
|
}
|
|
368
377
|
}
|
|
@@ -373,9 +382,12 @@ public class SecretsFeature extends BaseFeature {
|
|
|
373
382
|
}
|
|
374
383
|
}
|
|
375
384
|
|
|
376
|
-
|
|
385
|
+
// resolveonce resolves once, reporting whether a credential came out of
|
|
386
|
+
// it. That boolean is the whole of what resolve() needs to tell a
|
|
387
|
+
// cacheable HIT from a miss it must not keep.
|
|
388
|
+
private boolean resolveonce() {
|
|
377
389
|
if (this.sek == null) {
|
|
378
|
-
return;
|
|
390
|
+
return false;
|
|
379
391
|
}
|
|
380
392
|
|
|
381
393
|
// tryget: null is a MISS (the chain had nothing), an exception is an
|
|
@@ -392,7 +404,7 @@ public class SecretsFeature extends BaseFeature {
|
|
|
392
404
|
// unreachable.)
|
|
393
405
|
this.cred = found == null ? "" : found;
|
|
394
406
|
}
|
|
395
|
-
return;
|
|
407
|
+
return found != null;
|
|
396
408
|
}
|
|
397
409
|
|
|
398
410
|
// Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -416,10 +428,23 @@ public class SecretsFeature extends BaseFeature {
|
|
|
416
428
|
// A starting access token was supplied. Spend it: if it is stale the
|
|
417
429
|
// API answers with an expiry status and the transport wrapper buys
|
|
418
430
|
// another, which is the same path expiry takes anyway.
|
|
419
|
-
return;
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
435
|
+
// purchase is a credential-bearing call: the refresh token goes to the
|
|
436
|
+
// token endpoint in the request body. withrefresh honours suppression
|
|
437
|
+
// for the RETRY, but it runs after this - by then the refresh token has
|
|
438
|
+
// already left the process, and no later check can call it back. The
|
|
439
|
+
// suppression has to be honoured here, before the first purchase, or it
|
|
440
|
+
// only ever half-held.
|
|
441
|
+
if (this.liveopts.get("auth") == null) {
|
|
442
|
+
return false;
|
|
420
443
|
}
|
|
421
444
|
|
|
422
445
|
buy();
|
|
446
|
+
|
|
447
|
+
return true;
|
|
423
448
|
}
|
|
424
449
|
|
|
425
450
|
// withrefresh buys a token and tries the request again when the API says
|
|
@@ -760,6 +760,62 @@ public class SecretsFeatureTest {
|
|
|
760
760
|
+ " the wire saw \"" + last.auth + "\"");
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
764
|
+
// feature used to override from the layer above.
|
|
765
|
+
//
|
|
766
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
767
|
+
// holding a HIT, and keeping the settled resolution after a miss meant
|
|
768
|
+
// the chain was never asked again for the life of the client. A secret
|
|
769
|
+
// provisioned after startup (a mounted file, a vault policy granted a
|
|
770
|
+
// minute late) was invisible forever, and the only workaround was giving
|
|
771
|
+
// up hit caching entirely.
|
|
772
|
+
@Test
|
|
773
|
+
public void active_aCachedMissIsReasked() {
|
|
774
|
+
final boolean[] present = {false};
|
|
775
|
+
final int[] calls = {0};
|
|
776
|
+
Wire wire = new Wire();
|
|
777
|
+
ProjectNameSDK client = liveClient(wire, secretsOpts(
|
|
778
|
+
List.of(new TestProvider((name) -> {
|
|
779
|
+
calls[0]++;
|
|
780
|
+
return present[0] ? "LATEKEY01" : null;
|
|
781
|
+
}))));
|
|
782
|
+
|
|
783
|
+
client.direct(map("path", "/one"));
|
|
784
|
+
Call first = wire.api().get(0);
|
|
785
|
+
assertFalse(first.has && !"".equals(first.auth),
|
|
786
|
+
"the chain has nothing yet, so no credential should go out");
|
|
787
|
+
|
|
788
|
+
int asked = calls[0];
|
|
789
|
+
assertTrue(0 < asked);
|
|
790
|
+
|
|
791
|
+
// The secret is provisioned while the client is live.
|
|
792
|
+
present[0] = true;
|
|
793
|
+
client.direct(map("path", "/two"));
|
|
794
|
+
|
|
795
|
+
credentialIs(wire.api().get(wire.api().size() - 1).auth, "LATEKEY01");
|
|
796
|
+
assertTrue(asked < calls[0],
|
|
797
|
+
"the MISS was cached: a secret that appears later can never be picked up");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// The other half of the same rule: a HIT is still cached by default, so
|
|
801
|
+
// the fix above must not turn every request into a chain walk.
|
|
802
|
+
@Test
|
|
803
|
+
public void active_aCachedHitIsKept() {
|
|
804
|
+
final int[] calls = {0};
|
|
805
|
+
Wire wire = new Wire();
|
|
806
|
+
ProjectNameSDK client = liveClient(wire, secretsOpts(
|
|
807
|
+
List.of(new TestProvider((name) -> {
|
|
808
|
+
calls[0]++;
|
|
809
|
+
return "STABLEKEY01";
|
|
810
|
+
}))));
|
|
811
|
+
|
|
812
|
+
client.direct(map("path", "/one"));
|
|
813
|
+
client.direct(map("path", "/two"));
|
|
814
|
+
|
|
815
|
+
assertEquals(1, calls[0],
|
|
816
|
+
"a hit must be cached under the default cache: true");
|
|
817
|
+
}
|
|
818
|
+
|
|
763
819
|
// `auth: null` - the documented way to disable auth outright, which
|
|
764
820
|
// prepareAuth honours before it ever reads the apikey.
|
|
765
821
|
//
|
|
@@ -966,6 +1022,14 @@ public class SecretsFeatureTest {
|
|
|
966
1022
|
assertFalse(wire.api().get(0).has,
|
|
967
1023
|
"no credential may be sent when auth is suppressed, got \""
|
|
968
1024
|
+ wire.api().get(0).auth + "\"");
|
|
1025
|
+
|
|
1026
|
+
// AND NO PURCHASE. resolve() runs before withrefresh's suppression
|
|
1027
|
+
// check, so the refresh token used to go to the token endpoint in a
|
|
1028
|
+
// request body even here. Stopping the retry does not unsend it, and
|
|
1029
|
+
// only the token endpoint can see this.
|
|
1030
|
+
assertEquals(0, wire.token().size(),
|
|
1031
|
+
"auth null suppressed the credential but the refresh token was still"
|
|
1032
|
+
+ " POSTed to the exchange endpoint");
|
|
969
1033
|
}
|
|
970
1034
|
|
|
971
1035
|
@Test
|