@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
|
@@ -325,10 +325,18 @@ sub transport {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
|
|
328
|
-
# One resolution, shared by every request. A settled
|
|
329
|
-
#
|
|
330
|
-
#
|
|
331
|
-
#
|
|
328
|
+
# One resolution, shared by every request. A settled HIT is kept only when
|
|
329
|
+
# caching is on (`cache: false` means every resolve asks the chain again);
|
|
330
|
+
# a FAILURE is never kept, so a transient vault outage cannot poison the
|
|
331
|
+
# client permanently - the next request asks the chain again.
|
|
332
|
+
#
|
|
333
|
+
# A MISS is not kept either, however caching is set. That rule is
|
|
334
|
+
# sekreto's, not this feature's: `A miss is never cached: the next read
|
|
335
|
+
# asks again`, in sekreto's own source. Keeping a settled miss here would
|
|
336
|
+
# override that from the layer above, and a secret provisioned after
|
|
337
|
+
# startup - a mounted file, a policy granted a minute late - would never be
|
|
338
|
+
# picked up for the life of the client. `cache` is about caching a HIT; it
|
|
339
|
+
# was never a promise to keep saying no.
|
|
332
340
|
#
|
|
333
341
|
# Returns an error MESSAGE (a string) or undef.
|
|
334
342
|
sub resolve {
|
|
@@ -337,23 +345,26 @@ sub resolve {
|
|
|
337
345
|
return $self->{initerr} if defined $self->{initerr};
|
|
338
346
|
return undef if $self->{resolved} && $self->{cache};
|
|
339
347
|
|
|
340
|
-
my $err = $self->_resolve_once();
|
|
341
|
-
$self->{resolved} = defined($err) ? 0 : 1;
|
|
348
|
+
my ($err, $hit) = $self->_resolve_once();
|
|
349
|
+
$self->{resolved} = (defined($err) || !$hit) ? 0 : 1;
|
|
342
350
|
|
|
343
351
|
return $err;
|
|
344
352
|
}
|
|
345
353
|
|
|
346
354
|
|
|
355
|
+
# Resolve once, answering ($err, $hit): whether a credential came out of
|
|
356
|
+
# it. That flag is the whole of what resolve needs to tell a cacheable HIT
|
|
357
|
+
# from a miss it must not keep.
|
|
347
358
|
sub _resolve_once {
|
|
348
359
|
my ($self) = @_;
|
|
349
360
|
|
|
350
|
-
return undef unless defined $self->{sek};
|
|
361
|
+
return (undef, 0) unless defined $self->{sek};
|
|
351
362
|
|
|
352
363
|
# sekreto's miss-vs-error rule, inherited: `try` returns undef on a MISS
|
|
353
364
|
# and dies on a provider ERROR.
|
|
354
365
|
my $found;
|
|
355
366
|
my $ok = eval { $found = $self->{sek}->try($self->{secretname}); 1 };
|
|
356
|
-
return _trim(defined $@ && '' ne "$@" ? "$@" : 'secrets: provider failed')
|
|
367
|
+
return (_trim(defined $@ && '' ne "$@" ? "$@" : 'secrets: provider failed'), 0)
|
|
357
368
|
if !$ok;
|
|
358
369
|
|
|
359
370
|
if (!defined $self->{exchange}) {
|
|
@@ -363,7 +374,7 @@ sub _resolve_once {
|
|
|
363
374
|
# way - it seats FIRST in the chain as a memory provider, so the chain
|
|
364
375
|
# HITS while one is set and this branch is unreachable.)
|
|
365
376
|
$self->{cred} = defined $found ? "$found" : '';
|
|
366
|
-
return undef;
|
|
377
|
+
return (undef, defined($found) ? 1 : 0);
|
|
367
378
|
}
|
|
368
379
|
|
|
369
380
|
# Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -381,13 +392,23 @@ sub _resolve_once {
|
|
|
381
392
|
# A starting access token was supplied. Spend it: if it is stale the API
|
|
382
393
|
# answers with an expiry status and the wrapper buys another, which is
|
|
383
394
|
# the same path expiry takes anyway.
|
|
384
|
-
return undef if '' ne $self->{cred};
|
|
395
|
+
return (undef, 1) if '' ne $self->{cred};
|
|
396
|
+
|
|
397
|
+
# `auth => undef` is the documented way to send NO credential, and a
|
|
398
|
+
# purchase is a credential-bearing call: the refresh token goes to the
|
|
399
|
+
# token endpoint in the request body. _with_refresh honours suppression
|
|
400
|
+
# for the RETRY, but it runs after this - by then the refresh token has
|
|
401
|
+
# already left the process, and no later check can call it back. The
|
|
402
|
+
# suppression has to be honoured here, before the first purchase, or it
|
|
403
|
+
# only ever half-held.
|
|
404
|
+
return (undef, 0)
|
|
405
|
+
if !defined ProjectNameHelpers::gp($self->{liveopts}, 'auth');
|
|
385
406
|
|
|
386
407
|
my ($token, $err) = $self->_buy();
|
|
387
|
-
return $err if defined $err;
|
|
408
|
+
return ($err, 0) if defined $err;
|
|
388
409
|
|
|
389
410
|
$self->{cred} = $token;
|
|
390
|
-
return undef;
|
|
411
|
+
return (undef, 1);
|
|
391
412
|
}
|
|
392
413
|
|
|
393
414
|
|
|
@@ -510,6 +510,45 @@ SKIP: {
|
|
|
510
510
|
|
|
511
511
|
# --- cache -----------------------------------------------------------------
|
|
512
512
|
|
|
513
|
+
# A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this feature
|
|
514
|
+
# used to override from the layer above.
|
|
515
|
+
#
|
|
516
|
+
# DEFAULT caching here, which is the whole point: `cache: true` is about
|
|
517
|
+
# holding a HIT (the block after next pins that half), and keeping the
|
|
518
|
+
# settled resolution after a miss meant the chain was never asked again for
|
|
519
|
+
# the life of the client. A secret provisioned after startup (a mounted
|
|
520
|
+
# file, a vault policy granted a minute late) was invisible forever.
|
|
521
|
+
{
|
|
522
|
+
clear_env();
|
|
523
|
+
|
|
524
|
+
my $calls = 0;
|
|
525
|
+
my $present = 0;
|
|
526
|
+
my $late = {
|
|
527
|
+
'lookup' => sub { $calls++; return $present ? 'LATEKEY01' : undef },
|
|
528
|
+
'describe' => sub { 'late:test' },
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
my $stub = counting_fetch();
|
|
532
|
+
my $sdk = secrets_sdk($stub, { 'providers' => [$late] });
|
|
533
|
+
|
|
534
|
+
$sdk->direct({ 'path' => '/one' });
|
|
535
|
+
no_credential($stub->{calls}[0]{auth},
|
|
536
|
+
'the chain has nothing yet, so no credential should go out');
|
|
537
|
+
|
|
538
|
+
my $asked = $calls;
|
|
539
|
+
ok(0 < $asked, 'the chain was asked at least once');
|
|
540
|
+
|
|
541
|
+
# The secret is provisioned while the client is live.
|
|
542
|
+
$present = 1;
|
|
543
|
+
|
|
544
|
+
$sdk->direct({ 'path' => '/two' });
|
|
545
|
+
|
|
546
|
+
ok($asked < $calls,
|
|
547
|
+
'the MISS was cached: a secret that appears later can never be picked up');
|
|
548
|
+
credential_is($stub->{calls}[1]{auth}, 'LATEKEY01', 'the late secret goes out');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
513
552
|
{
|
|
514
553
|
clear_env();
|
|
515
554
|
|
|
@@ -907,6 +946,14 @@ sub exchange_sdk {
|
|
|
907
946
|
'exchange: a suppressed request must not be retried');
|
|
908
947
|
no_credential(api_calls($stub)->[0]{auth},
|
|
909
948
|
'exchange: no credential may be sent when auth is suppressed');
|
|
949
|
+
|
|
950
|
+
# AND NO PURCHASE. resolve runs before _with_refresh's suppression check,
|
|
951
|
+
# so the refresh token used to go to the token endpoint in a request body
|
|
952
|
+
# even here. Stopping the retry does not unsend it, and only the token
|
|
953
|
+
# endpoint can see this.
|
|
954
|
+
is(scalar @{ token_calls($stub) }, 0,
|
|
955
|
+
'exchange: auth undef suppressed the credential but the refresh token '
|
|
956
|
+
. 'was still POSTed to the exchange endpoint');
|
|
910
957
|
}
|
|
911
958
|
|
|
912
959
|
{
|
|
@@ -309,6 +309,14 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
|
|
|
309
309
|
// is never cached, so a transient vault outage does not poison the
|
|
310
310
|
// client after the vault recovers.
|
|
311
311
|
//
|
|
312
|
+
// A MISS is not cached either, however caching is set. That rule is
|
|
313
|
+
// sekreto's, not this feature's: `A miss is never cached: the next read
|
|
314
|
+
// asks again`, in sekreto's own source. Caching a settled miss here
|
|
315
|
+
// would override that from the layer above, and a secret provisioned
|
|
316
|
+
// after startup - a mounted file, a policy granted a minute late -
|
|
317
|
+
// would never be picked up for the life of the client. `cache` is about
|
|
318
|
+
// caching a HIT; it was never a promise to keep saying no.
|
|
319
|
+
//
|
|
312
320
|
// Answers the error rather than throwing it: the caller is the
|
|
313
321
|
// transport gate, whose contract is a [value, err] pair.
|
|
314
322
|
public function resolve(): ?\Throwable
|
|
@@ -321,22 +329,25 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
|
|
|
321
329
|
}
|
|
322
330
|
|
|
323
331
|
try {
|
|
324
|
-
$this->resolve_once();
|
|
332
|
+
$hit = $this->resolve_once();
|
|
325
333
|
} catch (\Throwable $e) {
|
|
326
334
|
return $e;
|
|
327
335
|
}
|
|
328
336
|
|
|
329
|
-
if ($this->cache) {
|
|
337
|
+
if ($this->cache && $hit) {
|
|
330
338
|
$this->resolved = true;
|
|
331
339
|
}
|
|
332
340
|
|
|
333
341
|
return null;
|
|
334
342
|
}
|
|
335
343
|
|
|
336
|
-
|
|
344
|
+
// Resolve once, answering whether a credential came out of it. That
|
|
345
|
+
// bool is the whole of what resolve() needs to tell a cacheable HIT
|
|
346
|
+
// from a miss it must not cache.
|
|
347
|
+
private function resolve_once(): bool
|
|
337
348
|
{
|
|
338
349
|
if (null === $this->sekreto) {
|
|
339
|
-
return;
|
|
350
|
+
return false;
|
|
340
351
|
}
|
|
341
352
|
|
|
342
353
|
// Miss-vs-error: `try` answers null for "no store has it" and
|
|
@@ -355,7 +366,7 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
|
|
|
355
366
|
// chain as a memory provider, so the chain HITS while one is
|
|
356
367
|
// set and the miss branch is unreachable.)
|
|
357
368
|
$this->setcred($found);
|
|
358
|
-
return;
|
|
369
|
+
return null !== $found;
|
|
359
370
|
}
|
|
360
371
|
|
|
361
372
|
// Exchanging: what the chain resolved is the REFRESH token, kept
|
|
@@ -377,10 +388,23 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
|
|
|
377
388
|
// A starting access token was supplied. Spend it: if it is
|
|
378
389
|
// stale the API answers with an expiry status and the wrapper
|
|
379
390
|
// buys another, which is the same path expiry takes anyway.
|
|
380
|
-
return;
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// `auth: null` is the documented way to send NO credential, and a
|
|
395
|
+
// purchase is a credential-bearing call: the refresh token goes to
|
|
396
|
+
// the token endpoint in the request body. with_refresh honours
|
|
397
|
+
// suppression for the RETRY, but it runs after this - by then the
|
|
398
|
+
// refresh token has already left the process, and no later check
|
|
399
|
+
// can call it back. The suppression has to be honoured here, before
|
|
400
|
+
// the first purchase, or it only ever half-held.
|
|
401
|
+
if (null === ($this->liveopts['auth'] ?? null)) {
|
|
402
|
+
return false;
|
|
381
403
|
}
|
|
382
404
|
|
|
383
405
|
$this->buy();
|
|
406
|
+
|
|
407
|
+
return true;
|
|
384
408
|
}
|
|
385
409
|
|
|
386
410
|
// Buy a token and try the request again when the API says the current
|
|
@@ -595,6 +595,74 @@ class ProjectNameSecretsTest extends TestCase
|
|
|
595
595
|
'the wire saw ' . var_export($last['auth'], true));
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
+
// A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
|
|
599
|
+
// feature used to override from the layer above.
|
|
600
|
+
//
|
|
601
|
+
// DEFAULT caching here, which is the whole point: `cache: true` is
|
|
602
|
+
// about holding a HIT, and caching the settled resolution after a miss
|
|
603
|
+
// meant the chain was never asked again for the life of the client. A
|
|
604
|
+
// secret provisioned after startup (a mounted file, a vault policy
|
|
605
|
+
// granted a minute late) was invisible forever, and the only workaround
|
|
606
|
+
// was giving up hit caching entirely.
|
|
607
|
+
public function testACachedMissIsReasked(): void
|
|
608
|
+
{
|
|
609
|
+
$present = false;
|
|
610
|
+
$calls = 0;
|
|
611
|
+
$wire = new ProjectNameSecretsWire();
|
|
612
|
+
$client = $this->secretsClient($wire, [
|
|
613
|
+
'feature' => ['secrets' => [
|
|
614
|
+
'active' => true,
|
|
615
|
+
'providers' => [$this->customProvider(
|
|
616
|
+
function (string $_name) use (&$present, &$calls) {
|
|
617
|
+
$calls++;
|
|
618
|
+
return $present ? 'LATEKEY01' : null;
|
|
619
|
+
})],
|
|
620
|
+
]],
|
|
621
|
+
]);
|
|
622
|
+
|
|
623
|
+
$this->driveOp($client, $wire);
|
|
624
|
+
$first = $wire->api()[0];
|
|
625
|
+
$this->assertFalse($first['has'] && '' !== (string)$first['auth'],
|
|
626
|
+
'the chain has nothing yet, so no credential should go out');
|
|
627
|
+
|
|
628
|
+
$asked = $calls;
|
|
629
|
+
$this->assertGreaterThan(0, $asked);
|
|
630
|
+
|
|
631
|
+
// The secret is provisioned while the client is live.
|
|
632
|
+
$present = true;
|
|
633
|
+
|
|
634
|
+
$this->driveOp($client, $wire);
|
|
635
|
+
$api = $wire->api();
|
|
636
|
+
$this->assertCredential($api[count($api) - 1]['auth'], 'LATEKEY01');
|
|
637
|
+
|
|
638
|
+
$this->assertGreaterThan($asked, $calls,
|
|
639
|
+
'the MISS was cached: a secret that appears later can never be picked up');
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// The other half of the same rule: a HIT is still cached by default, so
|
|
643
|
+
// the fix above must not turn every request into a chain walk.
|
|
644
|
+
public function testACachedHitIsKept(): void
|
|
645
|
+
{
|
|
646
|
+
$calls = 0;
|
|
647
|
+
$wire = new ProjectNameSecretsWire();
|
|
648
|
+
$client = $this->secretsClient($wire, [
|
|
649
|
+
'feature' => ['secrets' => [
|
|
650
|
+
'active' => true,
|
|
651
|
+
'providers' => [$this->customProvider(
|
|
652
|
+
function (string $_name) use (&$calls) {
|
|
653
|
+
$calls++;
|
|
654
|
+
return 'STABLEKEY01';
|
|
655
|
+
})],
|
|
656
|
+
]],
|
|
657
|
+
]);
|
|
658
|
+
|
|
659
|
+
$this->driveOp($client, $wire);
|
|
660
|
+
$this->driveOp($client, $wire);
|
|
661
|
+
|
|
662
|
+
$this->assertSame(1, $calls,
|
|
663
|
+
'a hit must be cached under the default cache: true');
|
|
664
|
+
}
|
|
665
|
+
|
|
598
666
|
public function testAuthNullSuppressesTheCredentialChainOrNoChain(): void
|
|
599
667
|
{
|
|
600
668
|
$this->setenv('APIKEY', 'ENVKEY03');
|
|
@@ -708,6 +776,36 @@ class ProjectNameSecretsTest extends TestCase
|
|
|
708
776
|
$this->assertCredential($api[1]['auth'], 'ACCESS02');
|
|
709
777
|
}
|
|
710
778
|
|
|
779
|
+
// `auth: null` SUPPRESSES THE PURCHASE, not just the retry.
|
|
780
|
+
//
|
|
781
|
+
// resolve() runs before with_refresh's suppression check, so the
|
|
782
|
+
// refresh token used to go to the token endpoint in a request body even
|
|
783
|
+
// here. Stopping the retry does not unsend it - this asserts on the
|
|
784
|
+
// token endpoint, which is the half the API-header assertions cannot
|
|
785
|
+
// see.
|
|
786
|
+
public function testAuthNullBuysNoTokenAtAll(): void
|
|
787
|
+
{
|
|
788
|
+
$this->setenv('REFRESH_TOKEN', 'REFRESH01');
|
|
789
|
+
$wire = new ProjectNameSecretsWire();
|
|
790
|
+
$wire->apistatus = [401];
|
|
791
|
+
|
|
792
|
+
$client = $this->secretsClient($wire, [
|
|
793
|
+
'auth' => null,
|
|
794
|
+
'feature' => $this->secretsOpts([
|
|
795
|
+
'name' => 'refresh_token',
|
|
796
|
+
'exchange' => ['active' => true],
|
|
797
|
+
]),
|
|
798
|
+
]);
|
|
799
|
+
|
|
800
|
+
$this->driveOp($client, $wire);
|
|
801
|
+
|
|
802
|
+
$this->assertCount(0, $wire->token(),
|
|
803
|
+
'auth null suppressed the credential but the refresh token was ' .
|
|
804
|
+
'still POSTed to the exchange endpoint');
|
|
805
|
+
$this->assertFalse($wire->api()[0]['has'],
|
|
806
|
+
'no credential may be sent when auth is suppressed');
|
|
807
|
+
}
|
|
808
|
+
|
|
711
809
|
// A second refusal on a token bought moments ago is a real failure, not
|
|
712
810
|
// a spin: exactly one rebuy, and the refusal is returned as it stands.
|
|
713
811
|
public function testASecondRefusalIsReturnedAsIs(): void
|
|
@@ -201,21 +201,31 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
|
|
|
201
201
|
def PreSpec(self, _ctx):
|
|
202
202
|
return self.resolve()
|
|
203
203
|
|
|
204
|
-
# Resolve the apikey before the first request. A successful
|
|
204
|
+
# Resolve the apikey before the first request. A successful HIT is
|
|
205
205
|
# reused while caching is on; with `cache: False` the chain is asked
|
|
206
206
|
# again on every call - holding the first answer forever would make
|
|
207
207
|
# the documented option a lie. A provider ERROR raises (fails the op)
|
|
208
208
|
# and leaves nothing cached, so a transient vault outage does not
|
|
209
209
|
# poison the client after the vault recovers.
|
|
210
|
+
#
|
|
211
|
+
# A MISS is not marked resolved either, however caching is set. That
|
|
212
|
+
# rule is sekreto's, not this feature's: `A miss is never cached: the
|
|
213
|
+
# next read asks again`, in sekreto's own source. Marking a settled
|
|
214
|
+
# miss here would override that from the layer above, and a secret
|
|
215
|
+
# provisioned after startup - a mounted file, a policy granted a minute
|
|
216
|
+
# late - would never be picked up for the life of the client. `cache`
|
|
217
|
+
# is about caching a HIT; it was never a promise to keep saying no.
|
|
210
218
|
def resolve(self):
|
|
211
219
|
if self._resolved and self._cache:
|
|
212
220
|
return
|
|
213
|
-
self._resolve_once()
|
|
214
|
-
self._resolved = True
|
|
221
|
+
self._resolved = self._resolve_once()
|
|
215
222
|
|
|
223
|
+
# Resolve once, reporting whether a credential came out of it. That
|
|
224
|
+
# bool is the whole of what resolve() needs to tell a cacheable HIT
|
|
225
|
+
# from a miss it must not retain.
|
|
216
226
|
def _resolve_once(self):
|
|
217
227
|
if self._sekreto is None:
|
|
218
|
-
return
|
|
228
|
+
return False
|
|
219
229
|
|
|
220
230
|
# Miss-vs-error: `try_` answers None for "no store has it" and
|
|
221
231
|
# RAISES for "a store could not answer" - only the miss falls
|
|
@@ -242,7 +252,7 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
|
|
|
242
252
|
if self._client.options.get("apikey") == self._lastset:
|
|
243
253
|
self._client.options["apikey"] = ""
|
|
244
254
|
self._lastset = None
|
|
245
|
-
return
|
|
255
|
+
return found is not None
|
|
246
256
|
|
|
247
257
|
# Exchanging: what the chain resolved is the REFRESH token, kept
|
|
248
258
|
# for every later purchase. A miss is not fatal here - an explicit
|
|
@@ -255,10 +265,22 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
|
|
|
255
265
|
# A starting access token was supplied. Spend it: if it is
|
|
256
266
|
# stale the API answers 401 and the transport wrapper buys
|
|
257
267
|
# another, which is the same path expiry takes anyway.
|
|
258
|
-
return
|
|
268
|
+
return True
|
|
269
|
+
|
|
270
|
+
# `auth: None` is the documented way to send NO credential, and a
|
|
271
|
+
# purchase is a credential-bearing call: the refresh token goes to
|
|
272
|
+
# the token endpoint in the request body. _with_refresh honours
|
|
273
|
+
# suppression for the RETRY, but it runs after this - by then the
|
|
274
|
+
# refresh token has already left the process, and no later check
|
|
275
|
+
# can call it back. The suppression has to be honoured here, before
|
|
276
|
+
# the first purchase, or it only ever half-held.
|
|
277
|
+
if self._client.options_map().get("auth") is None:
|
|
278
|
+
return False
|
|
259
279
|
|
|
260
280
|
self._client.options["apikey"] = self._buy()
|
|
261
281
|
|
|
282
|
+
return True
|
|
283
|
+
|
|
262
284
|
# Buy an access token with the refresh token. (ts shares one in-flight
|
|
263
285
|
# purchase between concurrent async callers; python resolves
|
|
264
286
|
# synchronously, so the sharing that remains observable is the
|
|
@@ -107,6 +107,18 @@ class TestSecrets(unittest.TestCase):
|
|
|
107
107
|
self.assertEqual(1, len(transport.calls))
|
|
108
108
|
return transport.calls[0]["fetchdef"].get("headers") or {}
|
|
109
109
|
|
|
110
|
+
def _lastwire(self, sdk, transport):
|
|
111
|
+
"""Drive one operation and read the headers off the LAST call, so a
|
|
112
|
+
test can operate more than once on the same client."""
|
|
113
|
+
utility = sdk._utility
|
|
114
|
+
ctx = utility.make_context({"opname": "load"}, sdk.get_root_ctx())
|
|
115
|
+
utility.feature_hook(ctx, "PreSpec")
|
|
116
|
+
|
|
117
|
+
fetchdef = sdk.prepare({"path": "/"})
|
|
118
|
+
res, err = utility.fetcher(ctx, fetchdef["url"], fetchdef)
|
|
119
|
+
self.assertIsNone(err)
|
|
120
|
+
return transport.calls[-1]["fetchdef"].get("headers") or {}
|
|
121
|
+
|
|
110
122
|
def assert_credential(self, headers, token):
|
|
111
123
|
# The Authorization header carries the SPEC's credential prefix,
|
|
112
124
|
# which a TEMPLATE cannot know - assert on the CREDENTIAL and let
|
|
@@ -196,6 +208,71 @@ class TestSecrets(unittest.TestCase):
|
|
|
196
208
|
with self.assertRaises(RuntimeError):
|
|
197
209
|
utility.feature_hook(ctx, "PreSpec")
|
|
198
210
|
|
|
211
|
+
# --- a miss is not a cacheable answer ---
|
|
212
|
+
|
|
213
|
+
# sekreto's own rule, which this feature used to override from the
|
|
214
|
+
# layer above: `A miss is never cached: the next read asks again`.
|
|
215
|
+
#
|
|
216
|
+
# DEFAULT caching in both tests below, which is the whole point.
|
|
217
|
+
# `cache: True` is about holding a HIT; holding the settled miss meant
|
|
218
|
+
# the chain was never asked again for the life of the client, so a
|
|
219
|
+
# secret provisioned after startup (a mounted file, a vault policy
|
|
220
|
+
# granted a minute late) was invisible forever.
|
|
221
|
+
def test_a_cached_miss_is_reasked(self):
|
|
222
|
+
state = {"present": False, "calls": 0}
|
|
223
|
+
|
|
224
|
+
def lookup(name):
|
|
225
|
+
state["calls"] += 1
|
|
226
|
+
return "LATEKEY01" if state["present"] else None
|
|
227
|
+
|
|
228
|
+
sdk, transport = self._sdk({
|
|
229
|
+
"feature": {"secrets": {
|
|
230
|
+
"active": True,
|
|
231
|
+
"providers": [{"lookup": lookup, "describe": lambda: "late:test"}],
|
|
232
|
+
}},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
headers = self._lastwire(sdk, transport)
|
|
236
|
+
self.assertNotIn("authorization", headers,
|
|
237
|
+
"the chain has nothing yet, so no credential "
|
|
238
|
+
"should go out")
|
|
239
|
+
|
|
240
|
+
asked = state["calls"]
|
|
241
|
+
self.assertLess(0, asked)
|
|
242
|
+
|
|
243
|
+
# The secret is provisioned while the client is live.
|
|
244
|
+
state["present"] = True
|
|
245
|
+
|
|
246
|
+
headers = self._lastwire(sdk, transport)
|
|
247
|
+
self.assert_credential(headers, "LATEKEY01")
|
|
248
|
+
|
|
249
|
+
self.assertLess(asked, state["calls"],
|
|
250
|
+
"the MISS was cached: a secret that appears later "
|
|
251
|
+
"can never be picked up")
|
|
252
|
+
|
|
253
|
+
def test_a_cached_hit_is_kept(self):
|
|
254
|
+
# The other half of the same rule: a HIT is still cached by
|
|
255
|
+
# default, so the fix above must not turn every request into a
|
|
256
|
+
# chain walk.
|
|
257
|
+
state = {"calls": 0}
|
|
258
|
+
|
|
259
|
+
def lookup(name):
|
|
260
|
+
state["calls"] += 1
|
|
261
|
+
return "STABLEKEY01"
|
|
262
|
+
|
|
263
|
+
sdk, transport = self._sdk({
|
|
264
|
+
"feature": {"secrets": {
|
|
265
|
+
"active": True,
|
|
266
|
+
"providers": [{"lookup": lookup, "describe": lambda: "counting:test"}],
|
|
267
|
+
}},
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
self._lastwire(sdk, transport)
|
|
271
|
+
self._lastwire(sdk, transport)
|
|
272
|
+
|
|
273
|
+
self.assertEqual(1, state["calls"],
|
|
274
|
+
"a hit must be cached under the default cache: True")
|
|
275
|
+
|
|
199
276
|
# --- auth: None suppression ---
|
|
200
277
|
|
|
201
278
|
def test_auth_none_still_suppresses_the_credential(self):
|
|
@@ -283,6 +360,43 @@ class TestSecretsReviewPins(unittest.TestCase):
|
|
|
283
360
|
srv.shutdown()
|
|
284
361
|
srv.server_close()
|
|
285
362
|
|
|
363
|
+
def test_auth_none_buys_no_token_at_all(self):
|
|
364
|
+
# `auth: None` SUPPRESSES THE PURCHASE, not just the retry.
|
|
365
|
+
#
|
|
366
|
+
# resolve() runs before _with_refresh's suppression check, so the
|
|
367
|
+
# refresh token used to go to the token endpoint in a request body
|
|
368
|
+
# even here. Stopping the retry does not unsend it - and only the
|
|
369
|
+
# token endpoint can see this, which is why the API-header
|
|
370
|
+
# assertion above is not enough.
|
|
371
|
+
tricky = "REFRESH01"
|
|
372
|
+
_TokenEndpoint.refresh_seen = None
|
|
373
|
+
|
|
374
|
+
srv = http.server.HTTPServer(("127.0.0.1", 0), _TokenEndpoint)
|
|
375
|
+
thread = threading.Thread(target=srv.serve_forever, daemon=True)
|
|
376
|
+
thread.start()
|
|
377
|
+
try:
|
|
378
|
+
sdk = secrets_sdk({
|
|
379
|
+
"base": "http://127.0.0.1:%d/api" % srv.server_port,
|
|
380
|
+
"auth": None,
|
|
381
|
+
"feature": {"secrets": {
|
|
382
|
+
"active": True,
|
|
383
|
+
"name": "refresh_token",
|
|
384
|
+
"exchange": {"active": True, "refresh": tricky},
|
|
385
|
+
}},
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
utility = sdk._utility
|
|
389
|
+
ctx = utility.make_context({"opname": "load"}, sdk.get_root_ctx())
|
|
390
|
+
utility.feature_hook(ctx, "PreSpec")
|
|
391
|
+
|
|
392
|
+
self.assertIsNone(_TokenEndpoint.refresh_seen,
|
|
393
|
+
"auth: None suppressed the credential but the "
|
|
394
|
+
"refresh token was still POSTed to the "
|
|
395
|
+
"exchange endpoint")
|
|
396
|
+
finally:
|
|
397
|
+
srv.shutdown()
|
|
398
|
+
srv.server_close()
|
|
399
|
+
|
|
286
400
|
def test_uncached_miss_retracts_the_credential(self):
|
|
287
401
|
# `cache: False` re-asks the chain per operation; when a provider
|
|
288
402
|
# that answered once reports a MISS (a revoked secret), the value
|
|
@@ -222,11 +222,19 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
|
|
|
222
222
|
_with_refresh(ctx, url, fetchdef, inner)
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
-
# One resolution, shared by every concurrent caller. A settled
|
|
225
|
+
# One resolution, shared by every concurrent caller. A settled HIT is
|
|
226
226
|
# kept only when caching is on (`cache: false` means every request asks
|
|
227
227
|
# the chain again); a FAILURE is never cached, so a transient vault
|
|
228
228
|
# outage does not poison the client after the vault recovers.
|
|
229
229
|
#
|
|
230
|
+
# A MISS is not kept either, however caching is set. That rule is
|
|
231
|
+
# sekreto's, not this feature's: `A miss is never cached: the next read
|
|
232
|
+
# asks again`, in sekreto's own source. Keeping a settled miss here would
|
|
233
|
+
# override that from the layer above, and a secret provisioned after
|
|
234
|
+
# startup - a mounted file, a policy granted a minute late - would never
|
|
235
|
+
# be picked up for the life of the client. `cache` is about caching a
|
|
236
|
+
# HIT; it was never a promise to keep saying no.
|
|
237
|
+
#
|
|
230
238
|
# Answers the error rather than raising it: the caller is the transport
|
|
231
239
|
# gate, whose contract is a [value, err] pair.
|
|
232
240
|
def resolve
|
|
@@ -236,20 +244,23 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
|
|
|
236
244
|
return nil if @resolved
|
|
237
245
|
|
|
238
246
|
begin
|
|
239
|
-
_resolve_once
|
|
247
|
+
hit = _resolve_once
|
|
240
248
|
rescue StandardError => e
|
|
241
249
|
return e
|
|
242
250
|
end
|
|
243
251
|
|
|
244
|
-
@resolved = true if @cache
|
|
252
|
+
@resolved = true if @cache && hit
|
|
245
253
|
nil
|
|
246
254
|
end
|
|
247
255
|
end
|
|
248
256
|
|
|
249
257
|
private
|
|
250
258
|
|
|
259
|
+
# Resolve once, answering whether a credential came out of it. That
|
|
260
|
+
# boolean is the whole of what resolve needs to tell a cacheable HIT from
|
|
261
|
+
# a miss it must not keep.
|
|
251
262
|
def _resolve_once
|
|
252
|
-
return if @sekreto.nil?
|
|
263
|
+
return false if @sekreto.nil?
|
|
253
264
|
|
|
254
265
|
# Miss-vs-error: `try` answers nil for "no store has it" and RAISES for
|
|
255
266
|
# "a store could not answer" - only the miss falls through.
|
|
@@ -264,7 +275,7 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
|
|
|
264
275
|
# the chain HITS while one is set and the miss branch is
|
|
265
276
|
# unreachable.)
|
|
266
277
|
_setcred(found)
|
|
267
|
-
return
|
|
278
|
+
return !found.nil?
|
|
268
279
|
end
|
|
269
280
|
|
|
270
281
|
# Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
@@ -283,9 +294,20 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
|
|
|
283
294
|
# A starting access token was supplied. Spend it: if it is stale the
|
|
284
295
|
# API answers with an expiry status and the wrapper buys another, which
|
|
285
296
|
# is the same path expiry takes anyway.
|
|
286
|
-
return unless apikey.nil? || apikey.empty?
|
|
297
|
+
return true unless apikey.nil? || apikey.empty?
|
|
298
|
+
|
|
299
|
+
# `auth: nil` is the documented way to send NO credential, and a
|
|
300
|
+
# purchase is a credential-bearing call: the refresh token goes to the
|
|
301
|
+
# token endpoint in the request body. _with_refresh honours suppression
|
|
302
|
+
# for the RETRY, but it runs after this - by then the refresh token has
|
|
303
|
+
# already left the process, and no later check can call it back. The
|
|
304
|
+
# suppression has to be honoured here, before the first purchase, or it
|
|
305
|
+
# only ever half-held.
|
|
306
|
+
return false if @liveopts["auth"].nil?
|
|
287
307
|
|
|
288
308
|
_buy
|
|
309
|
+
|
|
310
|
+
true
|
|
289
311
|
end
|
|
290
312
|
|
|
291
313
|
# Buy a token and try the request again when the API says the current one
|