@voxgig/sdkgen 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +330 -0
- package/project/.sdk/model/target/c.aon +15 -2
- package/project/.sdk/model/target/cpp.aon +18 -0
- package/project/.sdk/model/target/lua.aon +17 -0
- package/project/.sdk/model/target/ocaml.aon +48 -5
- package/project/.sdk/model/target/zig.aon +30 -4
- package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
- package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
- package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
- package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
- package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
- package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
- package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
- package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
- package/project/.sdk/tm/c/Makefile +58 -9
- package/project/.sdk/tm/c/core/sdk.h +10 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
- package/project/.sdk/tm/c/feature/secrets.c +797 -0
- package/project/.sdk/tm/c/feature/secrets.h +46 -0
- package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
- package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
- package/project/.sdk/tm/cpp/Makefile +48 -4
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
- package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
- package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
- package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
- package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
- package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
- package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
- package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
- package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
- package/project/.sdk/tm/lua/Makefile +15 -4
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
- package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
- package/project/.sdk/tm/ocaml/Makefile +52 -8
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
- package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
- package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
- package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
- package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
- package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
- package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
- package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
- package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
- package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
- package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
// Secret access via a vendored @voxgig/sekreto provider chain, and the
|
|
2
|
+
// access-token exchange some APIs require on top of it. The c port of
|
|
3
|
+
// tm/ts/src/feature/secrets/SecretsFeature.ts, following the GO port's
|
|
4
|
+
// structure (tm/go/feature/secrets_feature.go) - same contract, c idiom.
|
|
5
|
+
//
|
|
6
|
+
// The SDK's `apikey` option keeps exactly its old meaning: an explicit
|
|
7
|
+
// credential given in code. This feature makes it ONE SOURCE among several
|
|
8
|
+
// rather than the only one: when active, the apikey is resolved through a
|
|
9
|
+
// sekreto chain in which the explicit option (when set) is the FIRST
|
|
10
|
+
// provider - a `memory` store named `options` - so an explicit value always
|
|
11
|
+
// wins, by sekreto's own first-hit rule rather than by special-case logic.
|
|
12
|
+
//
|
|
13
|
+
// MISS vs ERROR (sekreto's invariant): a provider MISS falls through - the
|
|
14
|
+
// op proceeds, unauthenticated if nothing else supplies a credential. A
|
|
15
|
+
// provider ERROR (unreachable vault, bad creds) must FAIL the op: a broken
|
|
16
|
+
// vault never degrades into an unauthenticated request.
|
|
17
|
+
//
|
|
18
|
+
// WHERE RESOLUTION HAPPENS, and why it is not the PreSpec hook. c's hook
|
|
19
|
+
// slot is `void (*hook)(Feature*, const char*, Context*)` (core/sdk.h): a
|
|
20
|
+
// hook cannot fail an operation the way ts's awaited rejection can - and
|
|
21
|
+
// sdk_direct / sdk_graphql run no feature hooks at all (Main.fragment.c's
|
|
22
|
+
// sdk_raw_request calls utility_fetch directly). So, exactly as in go and
|
|
23
|
+
// rust, resolution happens at the TRANSPORT SEAM: `Utility.fetcher`, the
|
|
24
|
+
// one place every wire path crosses (entity ops through make_request,
|
|
25
|
+
// the raw paths through sdk_raw_request, both reading the client's one
|
|
26
|
+
// Utility). The wrapper refuses to send while the last resolution stands
|
|
27
|
+
// failed, which is fail-closed for the entity pipeline and the raw paths
|
|
28
|
+
// alike, with one implementation.
|
|
29
|
+
//
|
|
30
|
+
// WHERE THE CREDENTIAL LIVES, and why it is not the options map. The ts
|
|
31
|
+
// reference writes `options.apikey`; this port does NOT. prepare_auth
|
|
32
|
+
// builds the header from the client's options on every request, and this
|
|
33
|
+
// feature rewrites that header at the seam from a value it holds itself -
|
|
34
|
+
// same construction, same suppression rules - so `sdk_options_map` stays
|
|
35
|
+
// exactly what the caller passed, and the raw paths and the entity path
|
|
36
|
+
// cannot disagree about it. (go's structural rule, kept here for the same
|
|
37
|
+
// reason even though c is single threaded.)
|
|
38
|
+
//
|
|
39
|
+
// EXCHANGE: some APIs will not take a long-lived credential at all. What
|
|
40
|
+
// the chain resolves is then a REFRESH token, which buys a short-lived
|
|
41
|
+
// ACCESS token from a token endpoint (`exchange.path`, relative to
|
|
42
|
+
// options.base); the access token is what every request carries, and when
|
|
43
|
+
// a response status in `exchange.statuses` (401) says it is spent the
|
|
44
|
+
// wrapper buys another and retries the same request once. Test mode buys
|
|
45
|
+
// nothing and answers with a deterministic fake token.
|
|
46
|
+
//
|
|
47
|
+
// THE c-SPECIFIC SEAMS, stated because the parity table cannot check them:
|
|
48
|
+
//
|
|
49
|
+
// * A live provider object cannot travel in the options: voxgig_value is
|
|
50
|
+
// a closed union (UNDEF|NULL|BOOL|INT|DOUBLE|STRING|LIST|MAP|FUNC|
|
|
51
|
+
// SENTINEL), so go's `providers: [&customProvider{}]` has no c spelling.
|
|
52
|
+
// The answer is the `custom` pseudo-kind: a providers entry that is a
|
|
53
|
+
// FUNC, or a map `{ kind: "custom", lookup: <FUNC>, name?: "..." }`,
|
|
54
|
+
// bridged by a sek_provider whose lookup calls the FUNC through
|
|
55
|
+
// call_vfn - the same shape options.system.fetch and the retry
|
|
56
|
+
// feature's injected sleep already use. The FUNC is handed the secret
|
|
57
|
+
// NAME and answers a STRING (a hit), Noval/Null (a MISS - the chain
|
|
58
|
+
// continues) or a map `{ "__err__": "..." }` (an ERROR - the operation
|
|
59
|
+
// fails). Conflating the last two is the failure this library exists
|
|
60
|
+
// to prevent.
|
|
61
|
+
//
|
|
62
|
+
// * The c core ships no HTTP client (utility/fetcher.c). The exchange
|
|
63
|
+
// therefore uses options.system.fetch when supplied - the seam every
|
|
64
|
+
// live c request already lives under - and otherwise
|
|
65
|
+
// `secrets_rawfetch`, which lives in the GENERATED feature/secrets/
|
|
66
|
+
// kinds.c: a libcurl round-trip when a plugin group is active, a
|
|
67
|
+
// transport error that says so when none is (see Config_c; the
|
|
68
|
+
// generated kinds.mk beside it is what links libcurl on the same
|
|
69
|
+
// condition).
|
|
70
|
+
//
|
|
71
|
+
// * sek_new is documented NOT REENTRANT (a file-scope slot for the
|
|
72
|
+
// duration of the call). It runs once, in the client constructor,
|
|
73
|
+
// which is single threaded by core/context.c's own declaration; the
|
|
74
|
+
// same declaration is what lets this port drop go's mutex / in-flight
|
|
75
|
+
// promise apparatus. The pool a chain is built from is never freed:
|
|
76
|
+
// the c SDK is never-free (core/sdk.h "MEMORY MODEL"), and plugin's
|
|
77
|
+
// C port holds its declarations in a process arena regardless.
|
|
78
|
+
|
|
79
|
+
#include "sdk.h"
|
|
80
|
+
|
|
81
|
+
#include "secrets.h"
|
|
82
|
+
|
|
83
|
+
#include "sekreto.h"
|
|
84
|
+
|
|
85
|
+
#include <stddef.h>
|
|
86
|
+
#include <stdio.h>
|
|
87
|
+
#include <stdlib.h>
|
|
88
|
+
#include <string.h>
|
|
89
|
+
|
|
90
|
+
// The exchange transport of last resort, defined in the GENERATED
|
|
91
|
+
// feature/secrets/kinds.c (Config_c) - libcurl when a plugin group is
|
|
92
|
+
// active, a "no transport" error otherwise. Prototyped here rather than in
|
|
93
|
+
// sdk.h because core/ must not know a gated feature's internals.
|
|
94
|
+
voxgig_value* secrets_rawfetch(Context* ctx, const char* url,
|
|
95
|
+
voxgig_value* fetchdef, PNError** err);
|
|
96
|
+
|
|
97
|
+
typedef struct {
|
|
98
|
+
const char* path;
|
|
99
|
+
const char* method;
|
|
100
|
+
const char* request;
|
|
101
|
+
const char* response;
|
|
102
|
+
int64_t* statuses;
|
|
103
|
+
size_t nstatuses;
|
|
104
|
+
int64_t retries;
|
|
105
|
+
} SecretsExchange;
|
|
106
|
+
|
|
107
|
+
typedef struct {
|
|
108
|
+
int64_t resolves; // chain resolutions that completed (hit or miss)
|
|
109
|
+
int64_t buys; // token purchases (test mode included)
|
|
110
|
+
int64_t refused; // requests the fail-closed gate refused to send
|
|
111
|
+
} SecretsTrack;
|
|
112
|
+
|
|
113
|
+
typedef struct {
|
|
114
|
+
Feature base;
|
|
115
|
+
char* name;
|
|
116
|
+
bool active;
|
|
117
|
+
voxgig_value* add_opts;
|
|
118
|
+
voxgig_value* options;
|
|
119
|
+
|
|
120
|
+
ProjectNameSDK* client;
|
|
121
|
+
// The LIVE options map (root ctx options): read for `apikey` and `auth`,
|
|
122
|
+
// never written.
|
|
123
|
+
voxgig_value* liveopts;
|
|
124
|
+
|
|
125
|
+
const char* secretname;
|
|
126
|
+
bool cache;
|
|
127
|
+
|
|
128
|
+
sek_pool* pool;
|
|
129
|
+
sek_sekreto* sek;
|
|
130
|
+
|
|
131
|
+
// sekreto's own message when construction failed; the gate reads it.
|
|
132
|
+
char* initerr;
|
|
133
|
+
|
|
134
|
+
// The RESOLVED credential (the access token when exchanging), held here
|
|
135
|
+
// and injected at the seam - never written into the options map.
|
|
136
|
+
char* cred;
|
|
137
|
+
// A cached resolution stands (cache: true only); a FAILURE never sets it,
|
|
138
|
+
// so the next request asks the chain again.
|
|
139
|
+
bool resolved;
|
|
140
|
+
|
|
141
|
+
SecretsExchange* exchange;
|
|
142
|
+
char* refresh;
|
|
143
|
+
|
|
144
|
+
SecretsTrack track;
|
|
145
|
+
} SecretsFeature;
|
|
146
|
+
|
|
147
|
+
typedef struct {
|
|
148
|
+
Fetcher* inner;
|
|
149
|
+
SecretsFeature* f;
|
|
150
|
+
} SecretsState;
|
|
151
|
+
|
|
152
|
+
// ---- the custom-provider bridge --------------------------------------------
|
|
153
|
+
|
|
154
|
+
typedef struct {
|
|
155
|
+
voxgig_value* fn;
|
|
156
|
+
sek_pool* pool;
|
|
157
|
+
} CustomData;
|
|
158
|
+
|
|
159
|
+
static sek_err custom_lookup(sek_provider* self, const char* name, char** out) {
|
|
160
|
+
CustomData* d = (CustomData*)self->data;
|
|
161
|
+
voxgig_value* got = call_vfn(d->fn, v_str(name));
|
|
162
|
+
*out = NULL;
|
|
163
|
+
if (voxgig_is_string(got)) {
|
|
164
|
+
*out = sek_strdup(d->pool, voxgig_as_string(got));
|
|
165
|
+
return NULL;
|
|
166
|
+
}
|
|
167
|
+
if (voxgig_is_map(got)) {
|
|
168
|
+
const char* emsg = get_str(got, "__err__");
|
|
169
|
+
if (NULL != emsg) return sek_strdup(d->pool, emsg);
|
|
170
|
+
}
|
|
171
|
+
// Noval, Null, or anything that is not a value: a MISS.
|
|
172
|
+
return NULL;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static const char* custom_describe(sek_provider* self) {
|
|
176
|
+
(void)self;
|
|
177
|
+
return "custom";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static sek_provider* custom_provider(sek_pool* pool, voxgig_value* fn) {
|
|
181
|
+
CustomData* d = (CustomData*)sek_alloc(pool, sizeof(CustomData));
|
|
182
|
+
d->fn = v_share(fn);
|
|
183
|
+
d->pool = pool;
|
|
184
|
+
return sek_provider_new(pool, custom_lookup, custom_describe, d);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---- options map -> sek_spec ------------------------------------------------
|
|
188
|
+
|
|
189
|
+
// The spec's own key names paired with where each lands in a sek_spec - the
|
|
190
|
+
// same table upstream's providers.c keeps (specof), because the two
|
|
191
|
+
// directions must not drift. Strings only, as upstream reads them; `values`,
|
|
192
|
+
// `kv` and `auth` are not strings and are handled below.
|
|
193
|
+
typedef struct {
|
|
194
|
+
const char* key;
|
|
195
|
+
size_t at;
|
|
196
|
+
} SpecField;
|
|
197
|
+
|
|
198
|
+
#define SF(name, member) {name, offsetof(sek_spec, member)}
|
|
199
|
+
#define SAF(name, member) {name, offsetof(sek_authspec, member)}
|
|
200
|
+
|
|
201
|
+
static const SpecField SPECFIELDS[] = {
|
|
202
|
+
SF("kind", kind), SF("name", name), SF("prefix", prefix), SF("file", file),
|
|
203
|
+
SF("dir", dir), SF("addr", addr), SF("token", token), SF("mount", mount),
|
|
204
|
+
SF("vaultnamespace", vaultnamespace), SF("command", command),
|
|
205
|
+
SF("profile", profile), SF("backend", backend), SF("reason", reason),
|
|
206
|
+
SF("namespace", namespace_), SF("home", home), SF("region", region),
|
|
207
|
+
SF("keyid", keyid), SF("secret", secret), SF("session", session),
|
|
208
|
+
SF("project", project), SF("vault", vault), SF("tenant", tenant),
|
|
209
|
+
SF("clientid", clientid), SF("clientsecret", clientsecret),
|
|
210
|
+
SF("loginaddr", loginaddr), SF("imdsaddr", imdsaddr),
|
|
211
|
+
SF("metadataaddr", metadataaddr), SF("apiversion", apiversion),
|
|
212
|
+
SF("config", config), SF("environment", environment), SF("path", path),
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
static const SpecField AUTHFIELDS[] = {
|
|
216
|
+
SAF("method", method), SAF("mount", mount), SAF("role", role), SAF("jwt", jwt),
|
|
217
|
+
SAF("jwtfile", jwtfile), SAF("roleid", roleid), SAF("secretid", secretid),
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// A providers entry that is a map, as a spec. Answers false, with the
|
|
221
|
+
// refusal in *why, when the map says `custom` but carries no callable.
|
|
222
|
+
static bool spec_of_map(sek_pool* pool, voxgig_value* m, sek_spec* spec, const char** why) {
|
|
223
|
+
*spec = sek_spec_new(NULL);
|
|
224
|
+
*why = NULL;
|
|
225
|
+
|
|
226
|
+
const char* kind = get_str(m, "kind");
|
|
227
|
+
if (NULL != kind && 0 == strcmp(kind, "custom")) {
|
|
228
|
+
voxgig_value* lookup = getp(m, "lookup");
|
|
229
|
+
if (!voxgig_is_func(lookup)) {
|
|
230
|
+
*why = "a `custom` provider needs a `lookup` callable";
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
spec->provider = custom_provider(pool, lookup);
|
|
234
|
+
const char* name = get_str(m, "name");
|
|
235
|
+
if (NULL != name && '\0' != name[0]) spec->name = sek_own(pool, name);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (size_t i = 0; i < sizeof(SPECFIELDS) / sizeof(SPECFIELDS[0]); i++) {
|
|
240
|
+
const char* held = get_str(m, SPECFIELDS[i].key);
|
|
241
|
+
if (NULL != held) {
|
|
242
|
+
*(const char**)((char*)spec + SPECFIELDS[i].at) = sek_own(pool, held);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
voxgig_value* values = getp(m, "values");
|
|
247
|
+
if (voxgig_is_map(values)) {
|
|
248
|
+
voxgig_map* vm = voxgig_as_map(values);
|
|
249
|
+
spec->values = sek_map_new(pool);
|
|
250
|
+
for (size_t i = 0; i < vm->len; i++) {
|
|
251
|
+
voxgig_value* held = vm->entries[i].value;
|
|
252
|
+
char* text = voxgig_is_string(held) ? strdup(voxgig_as_string(held))
|
|
253
|
+
: voxgig_stringify(held, 0);
|
|
254
|
+
sek_map_set(spec->values, vm->entries[i].key, sek_own(pool, text));
|
|
255
|
+
free(text);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
voxgig_value* kv = getp(m, "kv");
|
|
260
|
+
if (voxgig_is_number(kv)) {
|
|
261
|
+
spec->kv = (int)to_int(kv);
|
|
262
|
+
spec->haskv = 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
voxgig_value* auth = getp(m, "auth");
|
|
266
|
+
if (voxgig_is_map(auth)) {
|
|
267
|
+
sek_authspec* use = (sek_authspec*)sek_alloc(pool, sizeof(sek_authspec));
|
|
268
|
+
for (size_t i = 0; i < sizeof(AUTHFIELDS) / sizeof(AUTHFIELDS[0]); i++) {
|
|
269
|
+
const char* held = get_str(auth, AUTHFIELDS[i].key);
|
|
270
|
+
if (NULL != held) {
|
|
271
|
+
*(const char**)((char*)use + AUTHFIELDS[i].at) = sek_own(pool, held);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
spec->auth = use;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ---- credential injection ---------------------------------------------------
|
|
281
|
+
|
|
282
|
+
// Write the resolved credential into THIS request's authorization header,
|
|
283
|
+
// the way prepare_auth builds it (same options auth.prefix, same
|
|
284
|
+
// suppression rule), so the two cannot drift.
|
|
285
|
+
//
|
|
286
|
+
// `auth: null` is the documented way to send NO credential, and
|
|
287
|
+
// prepare_auth honours it by removing the header; so does this, and it
|
|
288
|
+
// never writes one - which is what keeps the exchange from transmitting
|
|
289
|
+
// exactly the credential the caller suppressed on a retry. An EMPTY
|
|
290
|
+
// credential leaves prepare_auth's header alone: nothing was resolved, so
|
|
291
|
+
// there is nothing to inject and nothing to retract that prepare_auth did
|
|
292
|
+
// not already decide.
|
|
293
|
+
static void secrets_reauth(SecretsFeature* f, voxgig_value* fetchdef) {
|
|
294
|
+
voxgig_value* headers = getp(fetchdef, "headers");
|
|
295
|
+
if (!voxgig_is_map(headers)) return;
|
|
296
|
+
|
|
297
|
+
voxgig_value* auth = getp(f->liveopts, "auth");
|
|
298
|
+
if (v_is_noval(auth) || v_is_null(auth)) {
|
|
299
|
+
voxgig_value* k = voxgig_new_string("authorization");
|
|
300
|
+
voxgig_delprop(headers, k);
|
|
301
|
+
voxgig_release(k);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (NULL == f->cred || '\0' == f->cred[0]) return;
|
|
306
|
+
|
|
307
|
+
const char* prefix = get_str(auth, "prefix");
|
|
308
|
+
if (NULL == prefix || '\0' == prefix[0]) {
|
|
309
|
+
setp(headers, "authorization", v_str(f->cred));
|
|
310
|
+
} else {
|
|
311
|
+
size_t n = strlen(prefix) + 1 + strlen(f->cred) + 1;
|
|
312
|
+
char* buf = (char*)malloc(n);
|
|
313
|
+
snprintf(buf, n, "%s %s", prefix, f->cred);
|
|
314
|
+
setp(headers, "authorization", v_str(buf));
|
|
315
|
+
free(buf);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static void secrets_setcred(SecretsFeature* f, const char* value) {
|
|
320
|
+
free(f->cred);
|
|
321
|
+
f->cred = strdup(NULL == value ? "" : value);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---- the exchange -----------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
// Buy an access token with the refresh token. Answers sekreto-style: a
|
|
327
|
+
// pool-owned message, or NULL for success with the token in f->cred.
|
|
328
|
+
static const char* secrets_buy(SecretsFeature* f, Context* ctx) {
|
|
329
|
+
SecretsExchange* x = f->exchange;
|
|
330
|
+
|
|
331
|
+
// TEST MODE BUYS NOTHING. The test feature replaces the transport so no
|
|
332
|
+
// request leaves the process; an exchange here would be the one HTTP
|
|
333
|
+
// call it could not stop, and it would need a live token endpoint for a
|
|
334
|
+
// suite whose whole point is not needing one. A deterministic,
|
|
335
|
+
// obviously-fake token instead - the same answer make_options gives a
|
|
336
|
+
// required server variable, for the same reason.
|
|
337
|
+
if (0 != strcmp(f->client->mode, "live")) {
|
|
338
|
+
size_t n = 5 + strlen(x->response) + 1;
|
|
339
|
+
char* fake = (char*)malloc(n);
|
|
340
|
+
snprintf(fake, n, "test-%s", x->response);
|
|
341
|
+
secrets_setcred(f, fake);
|
|
342
|
+
free(fake);
|
|
343
|
+
f->track.buys++;
|
|
344
|
+
return NULL;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (NULL == f->refresh || '\0' == f->refresh[0]) {
|
|
348
|
+
return sek_fmt(f->pool,
|
|
349
|
+
"secrets: no refresh token: the provider chain has no '%s', and "
|
|
350
|
+
"feature.secrets.exchange.refresh is unset", f->secretname);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
voxgig_value* options = sdk_options_map(f->client);
|
|
354
|
+
|
|
355
|
+
// The token endpoint is RELATIVE to the base, which already carries
|
|
356
|
+
// whatever account or tenant segment the server URL declares.
|
|
357
|
+
const char* base = get_str(options, "base");
|
|
358
|
+
base = base ? base : "";
|
|
359
|
+
size_t blen = strlen(base);
|
|
360
|
+
while (0 < blen && '/' == base[blen - 1]) blen--;
|
|
361
|
+
const char* path = x->path;
|
|
362
|
+
while ('/' == path[0]) path++;
|
|
363
|
+
char* url = sek_fmt(f->pool, "%.*s/%s", (int)blen, base, path);
|
|
364
|
+
|
|
365
|
+
// The body is MARSHALLED, never concatenated: a refresh token (or a
|
|
366
|
+
// configured request-field name) carrying a quote, backslash or newline
|
|
367
|
+
// must arrive as that literal value, not as malformed JSON. sekreto's
|
|
368
|
+
// own JSON writer does it.
|
|
369
|
+
sek_json* body = sek_json_obj(f->pool);
|
|
370
|
+
sek_json_set(f->pool, body, x->request, sek_json_str(f->pool, f->refresh));
|
|
371
|
+
char* bodytext = sek_json_stringify(f->pool, body);
|
|
372
|
+
|
|
373
|
+
voxgig_value* fetchdef = cmap(3,
|
|
374
|
+
"method", v_str(x->method),
|
|
375
|
+
"headers", cmap(1, "content-type", v_str("application/json")),
|
|
376
|
+
"body", v_str(bodytext));
|
|
377
|
+
|
|
378
|
+
// Deliberately NOT the SDK transport. The transport is what this feature
|
|
379
|
+
// wraps, and sending the token request back through it would recurse on
|
|
380
|
+
// the first expiry - and would route the exchange through the test mock,
|
|
381
|
+
// which knows nothing about it. options.system.fetch when the caller
|
|
382
|
+
// supplied one (the seam every live c request already uses), else the
|
|
383
|
+
// bundled transport of last resort from the generated kinds.c.
|
|
384
|
+
voxgig_value* sys_fetch = getpath2(options, "system", "fetch");
|
|
385
|
+
voxgig_value* res;
|
|
386
|
+
if (voxgig_is_func(sys_fetch)) {
|
|
387
|
+
res = call_vfn(sys_fetch, clist(2, v_str(url), v_share(fetchdef)));
|
|
388
|
+
const char* emsg = get_str(res, "__err__");
|
|
389
|
+
if (NULL != emsg) return sek_strdup(f->pool, emsg);
|
|
390
|
+
} else {
|
|
391
|
+
PNError* ferr = NULL;
|
|
392
|
+
res = secrets_rawfetch(ctx, url, fetchdef, &ferr);
|
|
393
|
+
if (NULL != ferr) return sek_strdup(f->pool, ferr->msg ? ferr->msg : "secrets: transport failed");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
int64_t status = to_int(getp(res, "status"));
|
|
397
|
+
if (200 > status || 300 <= status) {
|
|
398
|
+
return sek_fmt(f->pool, "secrets: token exchange failed: %lld from %s",
|
|
399
|
+
(long long)status, url);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
voxgig_value* jf = getp(res, "json");
|
|
403
|
+
voxgig_value* parsed;
|
|
404
|
+
if (voxgig_is_func(jf)) {
|
|
405
|
+
parsed = call_json(jf);
|
|
406
|
+
} else {
|
|
407
|
+
voxgig_value* raw = getp(res, "body");
|
|
408
|
+
parsed = voxgig_is_string(raw) ? json_parse(voxgig_as_string(raw)) : raw;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const char* token = get_str(parsed, x->response);
|
|
412
|
+
if (NULL == token || '\0' == token[0]) {
|
|
413
|
+
return sek_fmt(f->pool, "secrets: token exchange returned no '%s' field from %s",
|
|
414
|
+
x->response, url);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
secrets_setcred(f, token);
|
|
418
|
+
f->track.buys++;
|
|
419
|
+
return NULL;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
static bool secrets_spent(SecretsFeature* f, voxgig_value* res) {
|
|
423
|
+
int64_t status;
|
|
424
|
+
if (!fres_status(res, &status)) return false;
|
|
425
|
+
for (size_t i = 0; i < f->exchange->nstatuses; i++) {
|
|
426
|
+
if (f->exchange->statuses[i] == status) return true;
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ---- resolution -------------------------------------------------------------
|
|
432
|
+
|
|
433
|
+
// One resolution. A settled SUCCESS is kept only when caching is on
|
|
434
|
+
// (`cache: false` means every request asks the chain again, and sekreto's
|
|
435
|
+
// own cache is off with it); a FAILURE is never kept, so a transient vault
|
|
436
|
+
// outage never poisons the client - the next request asks again. Answers
|
|
437
|
+
// the provider's own message, or NULL.
|
|
438
|
+
static const char* secrets_resolve(SecretsFeature* f, Context* ctx) {
|
|
439
|
+
if (NULL != f->initerr) return f->initerr;
|
|
440
|
+
if (f->cache && f->resolved) return NULL;
|
|
441
|
+
if (NULL == f->sek) return NULL;
|
|
442
|
+
|
|
443
|
+
char* found = NULL;
|
|
444
|
+
sek_err err = sek_try(f->sek, f->secretname, &found);
|
|
445
|
+
if (NULL != err) {
|
|
446
|
+
// A provider ERROR fails the op (via the transport gate); only a MISS
|
|
447
|
+
// falls through.
|
|
448
|
+
return err;
|
|
449
|
+
}
|
|
450
|
+
f->track.resolves++;
|
|
451
|
+
|
|
452
|
+
if (NULL == f->exchange) {
|
|
453
|
+
// A hit is the credential. An UNCACHED miss after an earlier hit is a
|
|
454
|
+
// revocation: the chain now says no provider has the secret, so the
|
|
455
|
+
// resolved value must not keep going out on the wire. (An explicit
|
|
456
|
+
// apikey OPTION is never lost here - it seats FIRST in the chain as a
|
|
457
|
+
// memory provider, so the chain HITS while one is set.)
|
|
458
|
+
secrets_setcred(f, found);
|
|
459
|
+
f->resolved = true;
|
|
460
|
+
return NULL;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Exchanging: what the chain resolved is the REFRESH token, kept for
|
|
464
|
+
// every later purchase. A miss is not fatal here - an explicit `apikey`
|
|
465
|
+
// may already hold a usable access token, and the API is what gets to
|
|
466
|
+
// say whether it does.
|
|
467
|
+
free(f->refresh);
|
|
468
|
+
f->refresh = strdup(found ? found : "");
|
|
469
|
+
|
|
470
|
+
if ('\0' == f->cred[0]) {
|
|
471
|
+
// A starting access token supplied as the OPTION: read from the frozen
|
|
472
|
+
// options map (no feature ever writes it).
|
|
473
|
+
const char* held = get_str(f->liveopts, "apikey");
|
|
474
|
+
if (NULL != held && '\0' != held[0]) secrets_setcred(f, held);
|
|
475
|
+
}
|
|
476
|
+
if ('\0' != f->cred[0]) {
|
|
477
|
+
// Spend it: if it is stale the API answers with an expiry status and
|
|
478
|
+
// the wrapper buys another, which is the same path expiry takes anyway.
|
|
479
|
+
f->resolved = true;
|
|
480
|
+
return NULL;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const char* berr = secrets_buy(f, ctx);
|
|
484
|
+
if (NULL != berr) return berr;
|
|
485
|
+
f->resolved = true;
|
|
486
|
+
return NULL;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ---- the transport wrapper --------------------------------------------------
|
|
490
|
+
|
|
491
|
+
static voxgig_value* secrets_fetch(Fetcher* self, Context* ctx, const char* url,
|
|
492
|
+
voxgig_value* fetchdef, PNError** err) {
|
|
493
|
+
SecretsState* st = (SecretsState*)self->state;
|
|
494
|
+
SecretsFeature* f = st->f;
|
|
495
|
+
*err = NULL;
|
|
496
|
+
|
|
497
|
+
// FAIL-CLOSED, at the ONE seam every wire path crosses. Entity ops,
|
|
498
|
+
// sdk_direct, sdk_graphql and the exchange retries all come through
|
|
499
|
+
// here, so resolving HERE is what gives the raw paths - which run no
|
|
500
|
+
// feature hooks at all - the same credential the entity pipeline gets.
|
|
501
|
+
// A construction failure (initerr) or a provider ERROR refuses the
|
|
502
|
+
// request with sekreto's own message - never an unauthenticated send.
|
|
503
|
+
if (NULL != f->initerr) {
|
|
504
|
+
f->track.refused++;
|
|
505
|
+
*err = context_make_error(ctx, "secrets_init", f->initerr);
|
|
506
|
+
return NULL;
|
|
507
|
+
}
|
|
508
|
+
const char* rerr = secrets_resolve(f, ctx);
|
|
509
|
+
if (NULL != rerr) {
|
|
510
|
+
f->track.refused++;
|
|
511
|
+
*err = context_make_error(ctx, "secrets_resolve", rerr);
|
|
512
|
+
return NULL;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Inject the resolved credential into THIS request's header (the header
|
|
516
|
+
// was built by prepare_auth from the options apikey; the chain-resolved
|
|
517
|
+
// value lives in feature state instead).
|
|
518
|
+
secrets_reauth(f, fetchdef);
|
|
519
|
+
|
|
520
|
+
if (NULL == f->exchange) {
|
|
521
|
+
return st->inner->fn(st->inner, ctx, url, fetchdef, err);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// `auth: null` is a deliberately unauthenticated request: a refusal of it
|
|
525
|
+
// is not an expired token and cannot be fixed by buying one.
|
|
526
|
+
voxgig_value* auth = getp(f->liveopts, "auth");
|
|
527
|
+
if (v_is_noval(auth) || v_is_null(auth)) {
|
|
528
|
+
return st->inner->fn(st->inner, ctx, url, fetchdef, err);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
int64_t max = f->exchange->retries;
|
|
532
|
+
int64_t attempt = 0;
|
|
533
|
+
for (;;) {
|
|
534
|
+
// The credential THIS attempt goes out with, captured before it leaves:
|
|
535
|
+
// it is what tells a stale refusal apart from a fresh one.
|
|
536
|
+
char* used = strdup(f->cred);
|
|
537
|
+
|
|
538
|
+
PNError* e = NULL;
|
|
539
|
+
voxgig_value* out = st->inner->fn(st->inner, ctx, url, fetchdef, &e);
|
|
540
|
+
|
|
541
|
+
if (NULL != e || attempt >= max || !secrets_spent(f, out)) {
|
|
542
|
+
free(used);
|
|
543
|
+
*err = e;
|
|
544
|
+
return out;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// A token bought since this attempt left (single threaded, so only by
|
|
548
|
+
// a nested request from a callback) is spent before another is bought:
|
|
549
|
+
// a second exchange for a token that is already fresh is wasted, and
|
|
550
|
+
// on a provider that invalidates the previous credential on issuance
|
|
551
|
+
// it breaks this request's own retry.
|
|
552
|
+
if ('\0' == f->cred[0] || 0 == strcmp(f->cred, used)) {
|
|
553
|
+
const char* berr = secrets_buy(f, ctx);
|
|
554
|
+
if (NULL != berr) {
|
|
555
|
+
// The purchase failed: answer with the API's own refusal rather
|
|
556
|
+
// than this one. The caller asked for data, and the refusal is
|
|
557
|
+
// the more useful of the two - the exchange error is a symptom.
|
|
558
|
+
free(used);
|
|
559
|
+
return out;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
free(used);
|
|
563
|
+
|
|
564
|
+
secrets_reauth(f, fetchdef);
|
|
565
|
+
attempt++;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// ---- the feature ------------------------------------------------------------
|
|
570
|
+
|
|
571
|
+
static const char* secrets_name(Feature* f) { return ((SecretsFeature*)f)->name; }
|
|
572
|
+
static bool secrets_active(Feature* f) { return ((SecretsFeature*)f)->active; }
|
|
573
|
+
static voxgig_value* secrets_add_options(Feature* f) { return ((SecretsFeature*)f)->add_opts; }
|
|
574
|
+
|
|
575
|
+
static void secrets_fail(SecretsFeature* f, const char* msg) {
|
|
576
|
+
// The FIRST failure stands: it names the entry the caller has to fix.
|
|
577
|
+
if (NULL == f->initerr) f->initerr = strdup(msg);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Init is sync by feature contract: build the chain, never look anything up
|
|
581
|
+
// here.
|
|
582
|
+
static void secrets_init(Feature* fb, Context* ctx, voxgig_value* options) {
|
|
583
|
+
SecretsFeature* f = (SecretsFeature*)fb;
|
|
584
|
+
f->options = options;
|
|
585
|
+
f->active = fopt_bool(options, "active", false);
|
|
586
|
+
if (!f->active) return;
|
|
587
|
+
|
|
588
|
+
f->client = ctx->client;
|
|
589
|
+
f->liveopts = ctx->options;
|
|
590
|
+
|
|
591
|
+
// WRAP FIRST. The fail-closed gate needs the seam whatever happens
|
|
592
|
+
// below - a chain that fails to build must still refuse to send - so the
|
|
593
|
+
// wrapper is installed before anything here can fail, and the exchange
|
|
594
|
+
// (when on) additionally needs to SEE responses: expiry is only ever
|
|
595
|
+
// discovered from one, and this is the one place a response can be seen
|
|
596
|
+
// and the request tried again.
|
|
597
|
+
Utility* util = context_util(ctx);
|
|
598
|
+
SecretsState* st = (SecretsState*)calloc(1, sizeof(SecretsState));
|
|
599
|
+
st->inner = util->fetcher;
|
|
600
|
+
st->f = f;
|
|
601
|
+
Fetcher* wrapped = (Fetcher*)calloc(1, sizeof(Fetcher));
|
|
602
|
+
wrapped->fn = secrets_fetch;
|
|
603
|
+
wrapped->state = st;
|
|
604
|
+
util->fetcher = wrapped;
|
|
605
|
+
|
|
606
|
+
f->secretname = fopt_str(options, "name", "apikey");
|
|
607
|
+
f->cache = fopt_bool(options, "cache", true);
|
|
608
|
+
f->pool = sek_pool_new();
|
|
609
|
+
|
|
610
|
+
// Exchange config, normalised once. NULL when off, so every later
|
|
611
|
+
// decision is a NULL check.
|
|
612
|
+
voxgig_value* xopts = fopt_map(options, "exchange");
|
|
613
|
+
if (fopt_bool(xopts, "active", false)) {
|
|
614
|
+
SecretsExchange* x = (SecretsExchange*)calloc(1, sizeof(SecretsExchange));
|
|
615
|
+
x->path = fopt_str(xopts, "path", "auth/token");
|
|
616
|
+
x->method = fopt_str(xopts, "method", "POST");
|
|
617
|
+
x->request = fopt_str(xopts, "request", "refresh_token");
|
|
618
|
+
x->response = fopt_str(xopts, "response", "access_token");
|
|
619
|
+
x->retries = fopt_int(xopts, "retries", 1);
|
|
620
|
+
voxgig_value* sl = fopt_list(xopts, "statuses");
|
|
621
|
+
if (voxgig_is_list(sl) && 0 < voxgig_as_list(sl)->len) {
|
|
622
|
+
voxgig_list* l = voxgig_as_list(sl);
|
|
623
|
+
x->statuses = (int64_t*)calloc(l->len, sizeof(int64_t));
|
|
624
|
+
for (size_t i = 0; i < l->len; i++) {
|
|
625
|
+
if (voxgig_is_number(l->items[i])) x->statuses[x->nstatuses++] = to_int(l->items[i]);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (0 == x->nstatuses) {
|
|
629
|
+
x->statuses = (int64_t*)calloc(1, sizeof(int64_t));
|
|
630
|
+
x->statuses[0] = 401;
|
|
631
|
+
x->nstatuses = 1;
|
|
632
|
+
}
|
|
633
|
+
f->exchange = x;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// The explicit credential, when set, is the first store in the chain.
|
|
637
|
+
//
|
|
638
|
+
// WHICH option that is depends on the exchange. Without one, the secret
|
|
639
|
+
// being resolved IS the credential the transport sends, so `apikey` is
|
|
640
|
+
// it. With one, the secret is a REFRESH token and `apikey` means the
|
|
641
|
+
// opposite thing - an access token the caller already holds - so the
|
|
642
|
+
// explicit seat belongs to `exchange.refresh`, and apikey is left alone
|
|
643
|
+
// to serve as the starting access token (see secrets_resolve).
|
|
644
|
+
//
|
|
645
|
+
// Read DIRECTLY, not through fopt_str, which answers its default for an
|
|
646
|
+
// empty string: an explicitly EMPTY apikey means "defer to the chain".
|
|
647
|
+
const char* explicit = NULL;
|
|
648
|
+
if (NULL == f->exchange) {
|
|
649
|
+
explicit = get_str(f->liveopts, "apikey");
|
|
650
|
+
} else {
|
|
651
|
+
explicit = get_str(xopts, "refresh");
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
size_t cap = 4;
|
|
655
|
+
size_t count = 0;
|
|
656
|
+
sek_spec* specs = (sek_spec*)calloc(cap, sizeof(sek_spec));
|
|
657
|
+
#define SPEC_PUSH(s) do { \
|
|
658
|
+
if (count == cap) { cap *= 2; specs = (sek_spec*)realloc(specs, cap * sizeof(sek_spec)); } \
|
|
659
|
+
specs[count++] = (s); \
|
|
660
|
+
} while (0)
|
|
661
|
+
|
|
662
|
+
if (NULL != explicit && '\0' != explicit[0]) {
|
|
663
|
+
char* key = NULL;
|
|
664
|
+
sek_err kerr = sek_envkey(f->pool, f->secretname, "", &key);
|
|
665
|
+
if (NULL != kerr) {
|
|
666
|
+
secrets_fail(f, kerr);
|
|
667
|
+
} else {
|
|
668
|
+
sek_spec seat = sek_spec_new("memory");
|
|
669
|
+
seat.name = "options";
|
|
670
|
+
seat.values = sek_map_new(f->pool);
|
|
671
|
+
sek_map_set(seat.values, key, sek_own(f->pool, explicit));
|
|
672
|
+
SPEC_PUSH(seat);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
voxgig_value* providers = fopt_list(options, "providers");
|
|
677
|
+
if (voxgig_is_list(providers)) {
|
|
678
|
+
voxgig_list* pl = voxgig_as_list(providers);
|
|
679
|
+
for (size_t i = 0; i < pl->len; i++) {
|
|
680
|
+
voxgig_value* p = pl->items[i];
|
|
681
|
+
if (voxgig_is_func(p)) {
|
|
682
|
+
// A provider already built (as built as c can spell it): joins
|
|
683
|
+
// the chain as it is.
|
|
684
|
+
sek_spec live = sek_spec_new(NULL);
|
|
685
|
+
live.provider = custom_provider(f->pool, p);
|
|
686
|
+
SPEC_PUSH(live);
|
|
687
|
+
} else if (voxgig_is_map(p)) {
|
|
688
|
+
sek_spec spec;
|
|
689
|
+
const char* why = NULL;
|
|
690
|
+
if (spec_of_map(f->pool, p, &spec, &why)) {
|
|
691
|
+
SPEC_PUSH(spec);
|
|
692
|
+
} else {
|
|
693
|
+
char* shown = voxgig_stringify(p, 0);
|
|
694
|
+
secrets_fail(f, sek_fmt(f->pool,
|
|
695
|
+
"sekreto: not a provider or a provider spec: %s (%s)", shown, why));
|
|
696
|
+
free(shown);
|
|
697
|
+
}
|
|
698
|
+
} else {
|
|
699
|
+
// FAIL CLOSED, never drop. An entry that is neither a callable nor
|
|
700
|
+
// a spec map (a bare "hashicorp" where a spec was meant, a number,
|
|
701
|
+
// a null) must not leave the chain quietly shorter than the
|
|
702
|
+
// options say: sekreto's own wording lands in the init-failure
|
|
703
|
+
// gate, which refuses to send. A loop that skipped the entry would
|
|
704
|
+
// SHORTEN the chain instead of failing it - a misconfigured
|
|
705
|
+
// providers list then sends ordinary unauthenticated requests, the
|
|
706
|
+
// exact fail-open the gate exists to prevent.
|
|
707
|
+
char* shown = voxgig_stringify(p, 0);
|
|
708
|
+
secrets_fail(f, sek_fmt(f->pool,
|
|
709
|
+
"sekreto: not a provider or a provider spec: %s", shown));
|
|
710
|
+
free(shown);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
#undef SPEC_PUSH
|
|
715
|
+
|
|
716
|
+
if (NULL == f->initerr) {
|
|
717
|
+
// The plugin DEFINITIONS the model selected for this feature, from the
|
|
718
|
+
// generated feature/secrets/kinds.c through core/config.c's accessor.
|
|
719
|
+
// Upstream sekreto's contract since the registry was retired: a kind
|
|
720
|
+
// not passed here is unknown to this Sekreto, so the model's choice of
|
|
721
|
+
// plugin groups IS the SDK's provider vocabulary.
|
|
722
|
+
size_t ndefs = 0;
|
|
723
|
+
void** defs = feature_plugins("secrets", &ndefs);
|
|
724
|
+
|
|
725
|
+
sek_options sopts;
|
|
726
|
+
memset(&sopts, 0, sizeof(sopts));
|
|
727
|
+
sopts.providers = specs;
|
|
728
|
+
sopts.count = count;
|
|
729
|
+
sopts.plugins = (Definition**)defs;
|
|
730
|
+
sopts.plugincount = ndefs;
|
|
731
|
+
sopts.nocache = f->cache ? 0 : 1;
|
|
732
|
+
|
|
733
|
+
sek_sekreto* sek = NULL;
|
|
734
|
+
sek_err serr = sek_new(f->pool, &sopts, &sek);
|
|
735
|
+
if (NULL != serr) {
|
|
736
|
+
// Init cannot fail the construction the way ts's throwing init does;
|
|
737
|
+
// the transport gate refuses to send instead, which keeps a
|
|
738
|
+
// misconfigured chain fail-closed rather than silently
|
|
739
|
+
// unauthenticated.
|
|
740
|
+
secrets_fail(f, serr);
|
|
741
|
+
} else {
|
|
742
|
+
f->sek = sek;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
static void secrets_hook(Feature* f, const char* name, Context* ctx) {
|
|
748
|
+
(void)f; (void)name; (void)ctx;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
static voxgig_value* secrets_track(Feature* fb) {
|
|
752
|
+
SecretsFeature* f = (SecretsFeature*)fb;
|
|
753
|
+
return cmap(3,
|
|
754
|
+
"resolves", v_num((double)f->track.resolves),
|
|
755
|
+
"buys", v_num((double)f->track.buys),
|
|
756
|
+
"refused", v_num((double)f->track.refused));
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
static const FeatureVT SECRETS_VT = {
|
|
760
|
+
secrets_name, secrets_active, secrets_add_options, secrets_init, secrets_hook,
|
|
761
|
+
secrets_track,
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
Feature* feature_secrets_new(void) {
|
|
765
|
+
SecretsFeature* f = (SecretsFeature*)calloc(1, sizeof(SecretsFeature));
|
|
766
|
+
f->base.vt = &SECRETS_VT;
|
|
767
|
+
f->name = strdup("secrets");
|
|
768
|
+
f->active = true; // overridden by init from options
|
|
769
|
+
f->add_opts = NULL;
|
|
770
|
+
f->options = voxgig_new_undef();
|
|
771
|
+
f->cred = strdup("");
|
|
772
|
+
f->refresh = strdup("");
|
|
773
|
+
return (Feature*)f;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// ---- accessors (secrets.h) --------------------------------------------------
|
|
777
|
+
|
|
778
|
+
Feature* sdk_feature_secrets(ProjectNameSDK* sdk) {
|
|
779
|
+
if (NULL == sdk) return NULL;
|
|
780
|
+
for (size_t i = 0; i < sdk->features_len; i++) {
|
|
781
|
+
Feature* f = sdk->features[i];
|
|
782
|
+
if (0 == strcmp(f->vt->name(f), "secrets")) return f;
|
|
783
|
+
}
|
|
784
|
+
return NULL;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
struct sek_sekreto* feature_secrets_sekreto(Feature* f) {
|
|
788
|
+
return NULL == f ? NULL : ((SecretsFeature*)f)->sek;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const char* feature_secrets_credential(Feature* f) {
|
|
792
|
+
return NULL == f ? "" : ((SecretsFeature*)f)->cred;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const char* feature_secrets_initerr(Feature* f) {
|
|
796
|
+
return NULL == f ? NULL : ((SecretsFeature*)f)->initerr;
|
|
797
|
+
}
|