jsecrets.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "jlog.hpp"
  15. #include "jutil.hpp"
  16. #include "jexcept.hpp"
  17. #include "jmutex.hpp"
  18. #include "jfile.hpp"
  19. #include "jptree.hpp"
  20. #include "jerror.hpp"
  21. #include "jsecrets.hpp"
  22. #include "build-config.h"
  23. //including cpp-httplib single header file REST client
  24. // doesn't work with format-nonliteral as an error
  25. //
  26. #if defined(__clang__) || defined(__GNUC__)
  27. #pragma GCC diagnostic push
  28. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  29. #endif
  30. #ifdef _USE_OPENSSL
  31. #define CPPHTTPLIB_OPENSSL_SUPPORT
  32. #endif
  33. #undef INVALID_SOCKET
  34. #include "httplib.h"
  35. #if defined(__clang__) || defined(__GNUC__)
  36. #pragma GCC diagnostic pop
  37. #endif
  38. #include <vector>
  39. enum class CVaultKind { kv_v1, kv_v2 };
  40. CVaultKind getSecretType(const char *s)
  41. {
  42. if (isEmptyString(s))
  43. return CVaultKind::kv_v2;
  44. if (streq(s, "kv_v1"))
  45. return CVaultKind::kv_v1;
  46. return CVaultKind::kv_v2;
  47. }
  48. interface IVaultManager : extends IInterface
  49. {
  50. virtual bool getCachedSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  51. virtual bool requestSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  52. virtual bool getCachedSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  53. virtual bool requestSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  54. };
  55. static CriticalSection secretCacheCS;
  56. static Owned<IPropertyTree> secretCache;
  57. static Owned<IVaultManager> vaultManager;
  58. MODULE_INIT(INIT_PRIORITY_SYSTEM)
  59. {
  60. secretCache.setown(createPTree());
  61. return true;
  62. }
  63. MODULE_EXIT()
  64. {
  65. vaultManager.clear();
  66. secretCache.clear();
  67. }
  68. static void splitUrlAddress(const char *address, size_t len, StringBuffer &host, StringBuffer *port)
  69. {
  70. if (!address || len==0)
  71. return;
  72. const char *sep = (const char *)memchr(address, ':', len);
  73. if (!sep)
  74. host.append(len, address);
  75. else
  76. {
  77. host.append(sep - address, address);
  78. len = len - (sep - address) - 1;
  79. if (port)
  80. port->append(len, sep+1);
  81. else
  82. host.append(':').append(len, sep+1);
  83. }
  84. }
  85. static void splitUrlAuthority(const char *authority, size_t authorityLen, StringBuffer &user, StringBuffer &password, StringBuffer &host, StringBuffer *port)
  86. {
  87. if (!authority || authorityLen==0)
  88. return;
  89. const char *at = (const char *) memchr(authority, '@', authorityLen);
  90. if (!at)
  91. splitUrlAddress(authority, authorityLen, host, port);
  92. else
  93. {
  94. size_t userinfoLen = (at - authority);
  95. splitUrlAddress(at+1, authorityLen - userinfoLen - 1, host, port);
  96. const char *sep = (const char *) memchr(authority, ':', at - authority);
  97. if (!sep)
  98. user.append(at-authority, authority);
  99. else
  100. {
  101. user.append(sep-authority, authority);
  102. size_t passwordLen = (at - sep - 1);
  103. password.append(passwordLen, sep+1);
  104. }
  105. }
  106. }
  107. static inline void extractUrlProtocol(const char *&url, StringBuffer *scheme)
  108. {
  109. if (!url)
  110. throw makeStringException(-1, "Invalid empty URL");
  111. if (0 == strnicmp(url, "HTTPS://", 8))
  112. {
  113. url+=8;
  114. if (scheme)
  115. scheme->append("https://");
  116. }
  117. else if (0 == strnicmp(url, "HTTP://", 7))
  118. {
  119. url+=7;
  120. if (scheme)
  121. scheme->append("http://");
  122. }
  123. else
  124. throw MakeStringException(-1, "Invalid URL, protocol not recognized %s", url);
  125. }
  126. static void splitUrlSections(const char *url, const char * &authority, size_t &authorityLen, StringBuffer &fullpath, StringBuffer *scheme)
  127. {
  128. extractUrlProtocol(url, scheme);
  129. const char* path = strchr(url, '/');
  130. authority = url;
  131. if (!path)
  132. authorityLen = strlen(authority);
  133. else
  134. {
  135. authorityLen = path-url;
  136. fullpath.append(path);
  137. }
  138. }
  139. extern jlib_decl void splitFullUrl(const char *url, StringBuffer &user, StringBuffer &password, StringBuffer &host, StringBuffer &port, StringBuffer &path)
  140. {
  141. const char *authority = nullptr;
  142. size_t authorityLen = 0;
  143. splitUrlSections(url, authority, authorityLen, path, nullptr);
  144. splitUrlAuthority(authority, authorityLen, user, password, host, &port);
  145. }
  146. extern jlib_decl void splitUrlSchemeHostPort(const char *url, StringBuffer &user, StringBuffer &password, StringBuffer &schemeHostPort, StringBuffer &path)
  147. {
  148. const char *authority = nullptr;
  149. size_t authorityLen = 0;
  150. splitUrlSections(url, authority, authorityLen, path, &schemeHostPort);
  151. splitUrlAuthority(authority, authorityLen, user, password, schemeHostPort, nullptr);
  152. }
  153. //---------------------------------------------------------------------------------------------------------------------
  154. static StringBuffer secretDirectory;
  155. static CriticalSection secretCS;
  156. //there are various schemes for renewing kubernetes secrets and they are likely to vary greatly in how often
  157. // a secret gets updated this timeout determines the maximum amount of time before we'll pick up a change
  158. // 10 minutes for now we can change this as we gather more experience and user feedback
  159. static unsigned secretTimeoutMs = 10 * 60 * 1000;
  160. extern jlib_decl unsigned getSecretTimeout()
  161. {
  162. return secretTimeoutMs;
  163. }
  164. extern jlib_decl void setSecretTimeout(unsigned timeoutMs)
  165. {
  166. secretTimeoutMs = timeoutMs;
  167. }
  168. extern jlib_decl void setSecretMount(const char * path)
  169. {
  170. if (!path)
  171. {
  172. getPackageFolder(secretDirectory);
  173. addPathSepChar(secretDirectory).append("secrets");
  174. }
  175. else
  176. secretDirectory.set(path);
  177. }
  178. static inline bool checkSecretExpired(unsigned created)
  179. {
  180. if (!created)
  181. return false;
  182. unsigned age = msTick() - created;
  183. return age > getSecretTimeout();
  184. }
  185. class CVault
  186. {
  187. private:
  188. bool useKubernetesAuth = true;
  189. CVaultKind kind;
  190. CriticalSection vaultCS;
  191. Owned<IPropertyTree> cache;
  192. StringBuffer schemeHostPort;
  193. StringBuffer path;
  194. StringBuffer username;
  195. StringBuffer password;
  196. StringAttr name;
  197. StringAttr role;
  198. StringAttr token;
  199. public:
  200. CVault(IPropertyTree *vault)
  201. {
  202. cache.setown(createPTree());
  203. StringBuffer url;
  204. replaceEnvVariables(url, vault->queryProp("@url"), false);
  205. if (url.length())
  206. splitUrlSchemeHostPort(url.str(), username, password, schemeHostPort, path);
  207. name.set(vault->queryProp("@name"));
  208. kind = getSecretType(vault->queryProp("@kind"));
  209. if (vault->hasProp("@role"))
  210. role.set(vault->queryProp("@role"));
  211. else
  212. role.set("hpcc-vault-access");
  213. if (vault->hasProp("@client-secret"))
  214. {
  215. useKubernetesAuth = false;
  216. //for now only support direct access token. we can support other combinations for example login token, ldap login, etc later.
  217. Owned<IPropertyTree> clientSecret = getLocalSecret("system", vault->queryProp("@client-secret"));
  218. if (clientSecret)
  219. token.set(clientSecret->queryProp("token"));
  220. }
  221. }
  222. CVaultKind getVaultKind() const { return kind; }
  223. void kubernetesLogin()
  224. {
  225. CriticalBlock block(vaultCS);
  226. if (token.length())
  227. return;
  228. StringBuffer login_token;
  229. login_token.loadFile("/var/run/secrets/kubernetes.io/serviceaccount/token");
  230. if (login_token.length())
  231. {
  232. std::string json;
  233. json.append("{\"jwt\": \"").append(login_token.str()).append("\", \"role\": \"").append(role.str()).append("\"}");
  234. httplib::Client cli(schemeHostPort.str());
  235. if (username.length() && password.length())
  236. cli.set_basic_auth(username, password);
  237. httplib::Result res = cli.Post("/v1/auth/kubernetes/login", json, "application/json");
  238. if (res)
  239. {
  240. if (res->status == 200)
  241. {
  242. const char *response = res->body.c_str();
  243. if (!isEmptyString(response))
  244. {
  245. Owned<IPropertyTree> respTree = createPTreeFromJSONString(response);
  246. if (respTree)
  247. token.set(respTree->queryProp("auth/client_token"));
  248. }
  249. }
  250. else
  251. {
  252. Owned<IException> e = MakeStringException(0, "Vault kube auth error [%d](%d) - vault: %s - response: %s", res->status, res.error(), name.str(), res->body.c_str());
  253. OWARNLOG(e);
  254. throw e.getClear();
  255. }
  256. }
  257. }
  258. }
  259. bool getCachedSecret(CVaultKind &rkind, StringBuffer &content, const char *secret, const char *version)
  260. {
  261. CriticalBlock block(vaultCS);
  262. IPropertyTree *tree = cache->queryPropTree(secret);
  263. if (tree)
  264. {
  265. VStringBuffer vername("v.%s", isEmptyString(version) ? "latest" : version);
  266. IPropertyTree *envelope = tree->queryPropTree(vername);
  267. if (!envelope)
  268. return false;
  269. if (checkSecretExpired((unsigned) envelope->getPropInt("@created")))
  270. {
  271. tree->removeTree(envelope);
  272. return false;
  273. }
  274. const char *s = envelope->queryProp("");
  275. if (!isEmptyString(s))
  276. {
  277. rkind = kind;
  278. content.append(s);
  279. return true;
  280. }
  281. }
  282. return false;
  283. }
  284. void addCachedSecret(const char *content, const char *secret, const char *version)
  285. {
  286. VStringBuffer vername("v.%s", isEmptyString(version) ? "latest" : version);
  287. Owned<IPropertyTree> envelope = createPTree(vername);
  288. envelope->setPropInt("@created", (int) msTick());
  289. envelope->setProp("", content);
  290. {
  291. CriticalBlock block(vaultCS);
  292. IPropertyTree *parent = ensurePTree(cache, secret);
  293. parent->setPropTree(vername, envelope.getClear());
  294. }
  295. }
  296. bool requestSecret(CVaultKind &rkind, StringBuffer &content, const char *secret, const char *version)
  297. {
  298. if (isEmptyString(secret))
  299. return false;
  300. if (useKubernetesAuth && token.isEmpty())
  301. kubernetesLogin();
  302. if (token.isEmpty())
  303. {
  304. Owned<IException> e = MakeStringException(0, "Vault auth error - vault: %s - vault access token not provided", name.str());
  305. OERRLOG(e);
  306. throw e.getClear();
  307. }
  308. StringBuffer location(path);
  309. location.replaceString("${secret}", secret);
  310. location.replaceString("${version}", version ? version : "1");
  311. httplib::Client cli(schemeHostPort.str());
  312. if (username.length() && password.length())
  313. cli.set_basic_auth(username.str(), password.str());
  314. httplib::Headers headers = {
  315. { "X-Vault-Token", token.str() }
  316. };
  317. if (httplib::Result res = cli.Get(location, headers))
  318. {
  319. if (res->status == 200)
  320. {
  321. rkind = kind;
  322. content.append(res->body.c_str());
  323. addCachedSecret(content.str(), secret, version);
  324. return true;
  325. }
  326. else
  327. {
  328. DBGLOG("Vault %s error accessing secret %s.%s [%d](%d) - response: %s", name.str(), secret, version ? version : "", res->status, res.error(), res->body.c_str());
  329. }
  330. }
  331. return false;
  332. }
  333. };
  334. class CVaultSet
  335. {
  336. private:
  337. std::map<std::string, std::unique_ptr<CVault>> vaults;
  338. public:
  339. CVaultSet()
  340. {
  341. }
  342. void addVault(IPropertyTree *vault)
  343. {
  344. const char *name = vault->queryProp("@name");
  345. if (!isEmptyString(name))
  346. vaults.emplace(name, std::unique_ptr<CVault>(new CVault(vault)));
  347. }
  348. bool getCachedSecret(CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  349. {
  350. auto it = vaults.begin();
  351. for (; it != vaults.end(); it++)
  352. {
  353. if (it->second->getCachedSecret(kind, content, secret, version))
  354. return true;
  355. }
  356. return false;
  357. }
  358. bool requestSecret(CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  359. {
  360. auto it = vaults.begin();
  361. for (; it != vaults.end(); it++)
  362. {
  363. if (it->second->requestSecret(kind, content, secret, version))
  364. return true;
  365. }
  366. return false;
  367. }
  368. bool getCachedSecretFromVault(const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  369. {
  370. if (isEmptyString(vaultId))
  371. return false;
  372. auto it = vaults.find(vaultId);
  373. if (it == vaults.end())
  374. return false;
  375. return it->second->getCachedSecret(kind, content, secret, version);
  376. }
  377. bool requestSecretFromVault(const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  378. {
  379. if (isEmptyString(vaultId))
  380. return false;
  381. auto it = vaults.find(vaultId);
  382. if (it == vaults.end())
  383. return false;
  384. return it->second->requestSecret(kind, content, secret, version);
  385. }
  386. };
  387. class CVaultManager : public CInterfaceOf<IVaultManager>
  388. {
  389. private:
  390. std::map<std::string, std::unique_ptr<CVaultSet>> categories;
  391. public:
  392. CVaultManager()
  393. {
  394. IPropertyTree *config = nullptr;
  395. try
  396. {
  397. config = queryComponentConfig().queryPropTree("vaults");
  398. }
  399. catch (IException * e)
  400. {
  401. EXCLOG(e);
  402. e->Release();
  403. }
  404. if (!config)
  405. return;
  406. Owned<IPropertyTreeIterator> iter = config->getElements("*");
  407. ForEach (*iter)
  408. {
  409. IPropertyTree &vault = iter->query();
  410. const char *category = vault.queryName();
  411. auto it = categories.find(category);
  412. if (it == categories.end())
  413. {
  414. auto placed = categories.emplace(category, std::unique_ptr<CVaultSet>(new CVaultSet()));
  415. if (placed.second)
  416. it = placed.first;
  417. }
  418. if (it != categories.end())
  419. it->second->addVault(&vault);
  420. }
  421. }
  422. bool getCachedSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  423. {
  424. if (isEmptyString(category))
  425. return false;
  426. auto it = categories.find(category);
  427. if (it == categories.end())
  428. return false;
  429. return it->second->getCachedSecretFromVault(vaultId, kind, content, secret, version);
  430. }
  431. bool requestSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  432. {
  433. if (isEmptyString(category))
  434. return false;
  435. auto it = categories.find(category);
  436. if (it == categories.end())
  437. return false;
  438. return it->second->requestSecretFromVault(vaultId, kind, content, secret, version);
  439. }
  440. bool getCachedSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  441. {
  442. if (isEmptyString(category))
  443. return false;
  444. auto it = categories.find(category);
  445. if (it == categories.end())
  446. return false;
  447. return it->second->getCachedSecret(kind, content, secret, version);
  448. }
  449. bool requestSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  450. {
  451. if (isEmptyString(category))
  452. return false;
  453. auto it = categories.find(category);
  454. if (it == categories.end())
  455. return false;
  456. return it->second->requestSecret(kind, content, secret, version);
  457. }
  458. };
  459. IVaultManager *ensureVaultManager()
  460. {
  461. CriticalBlock block(secretCS);
  462. if (!vaultManager)
  463. vaultManager.setown(new CVaultManager());
  464. return vaultManager;
  465. }
  466. static IPropertyTree *getCachedLocalSecret(const char *category, const char *name)
  467. {
  468. if (isEmptyString(name))
  469. return nullptr;
  470. Owned<IPropertyTree> secret;
  471. {
  472. CriticalBlock block(secretCacheCS);
  473. IPropertyTree *tree = secretCache->queryPropTree(category);
  474. if (!tree)
  475. return nullptr;
  476. secret.setown(tree->getPropTree(name));
  477. if (secret)
  478. {
  479. if (checkSecretExpired((unsigned) secret->getPropInt("@created")))
  480. {
  481. secretCache->removeProp(name);
  482. return nullptr;
  483. }
  484. return secret.getClear();
  485. }
  486. }
  487. return nullptr;
  488. }
  489. static void addCachedLocalSecret(const char *category, const char *name, IPropertyTree *secret)
  490. {
  491. if (!secret || isEmptyString(name) || isEmptyString(category))
  492. return;
  493. secret->setPropInt("@created", (int)msTick());
  494. {
  495. CriticalBlock block(secretCacheCS);
  496. IPropertyTree *tree = ensurePTree(secretCache, category);
  497. tree->setPropTree(name, LINK(secret));
  498. }
  499. }
  500. static const char *ensureSecretDirectory()
  501. {
  502. CriticalBlock block(secretCS);
  503. if (secretDirectory.isEmpty())
  504. setSecretMount(nullptr);
  505. return secretDirectory;
  506. }
  507. static IPropertyTree *loadLocalSecret(const char *category, const char * name)
  508. {
  509. StringBuffer path;
  510. addPathSepChar(path.append(ensureSecretDirectory())).append(category).append(PATHSEPCHAR).append(name).append(PATHSEPCHAR);
  511. Owned<IDirectoryIterator> entries = createDirectoryIterator(path);
  512. if (!entries || !entries->first())
  513. return nullptr;
  514. Owned<IPropertyTree> tree = createPTree(name);
  515. tree->setPropInt("@created", (int) msTick());
  516. ForEach(*entries)
  517. {
  518. if (entries->isDir())
  519. continue;
  520. StringBuffer name;
  521. entries->getName(name);
  522. if (!validateXMLTag(name))
  523. continue;
  524. MemoryBuffer content;
  525. Owned<IFileIO> io = entries->get().open(IFOread);
  526. read(io, 0, (size32_t)-1, content);
  527. if (!content.length())
  528. continue;
  529. tree->setPropBin(name, content.length(), content.bufferBase());
  530. }
  531. addCachedLocalSecret(category, name, tree);
  532. return tree.getClear();
  533. }
  534. extern jlib_decl IPropertyTree *getLocalSecret(const char *category, const char * name)
  535. {
  536. Owned<IPropertyTree> tree = getCachedLocalSecret(category, name);
  537. if (tree)
  538. return tree.getClear();
  539. return loadLocalSecret(category, name);
  540. }
  541. static IPropertyTree *createPTreeFromVaultSecret(const char *content, CVaultKind kind)
  542. {
  543. if (isEmptyString(content))
  544. return nullptr;
  545. Owned<IPropertyTree> tree = createPTreeFromJSONString(content);
  546. if (!tree)
  547. return nullptr;
  548. switch (kind)
  549. {
  550. case CVaultKind::kv_v1:
  551. tree.setown(tree->getPropTree("data"));
  552. break;
  553. default:
  554. case CVaultKind::kv_v2:
  555. tree.setown(tree->getPropTree("data/data"));
  556. break;
  557. }
  558. return tree.getClear();
  559. }
  560. static IPropertyTree *getCachedVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  561. {
  562. CVaultKind kind;
  563. StringBuffer json;
  564. IVaultManager *vaultmgr = ensureVaultManager();
  565. if (isEmptyString(vaultId))
  566. {
  567. if (!vaultmgr->getCachedSecretByCategory(category, kind, json, name, version))
  568. return nullptr;
  569. }
  570. else
  571. {
  572. if (!vaultmgr->getCachedSecretFromVault(category, vaultId, kind, json, name, version))
  573. return nullptr;
  574. }
  575. return createPTreeFromVaultSecret(json.str(), kind);
  576. }
  577. static IPropertyTree *requestVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  578. {
  579. CVaultKind kind;
  580. StringBuffer json;
  581. IVaultManager *vaultmgr = ensureVaultManager();
  582. if (isEmptyString(vaultId))
  583. {
  584. if (!vaultmgr->requestSecretByCategory(category, kind, json, name, version))
  585. return nullptr;
  586. }
  587. else
  588. {
  589. if (!vaultmgr->requestSecretFromVault(category, vaultId, kind, json, name, version))
  590. return nullptr;
  591. }
  592. return createPTreeFromVaultSecret(json.str(), kind);
  593. }
  594. extern jlib_decl IPropertyTree *getVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  595. {
  596. CVaultKind kind;
  597. StringBuffer json;
  598. IVaultManager *vaultmgr = ensureVaultManager();
  599. if (isEmptyString(vaultId))
  600. {
  601. if (!vaultmgr->getCachedSecretByCategory(category, kind, json, name, version))
  602. vaultmgr->requestSecretByCategory(category, kind, json, name, version);
  603. }
  604. else
  605. {
  606. if (!vaultmgr->getCachedSecretFromVault(category, vaultId, kind, json, name, version))
  607. vaultmgr->requestSecretFromVault(category, vaultId, kind, json, name, version);
  608. }
  609. return createPTreeFromVaultSecret(json.str(), kind);
  610. }
  611. extern jlib_decl IPropertyTree *getSecret(const char *category, const char * name)
  612. {
  613. //check for any chached first
  614. Owned<IPropertyTree> secret = getCachedLocalSecret(category, name);
  615. if (!secret)
  616. secret.setown(getCachedVaultSecret(category, nullptr, name, nullptr));
  617. //now check local, then vaults
  618. if (!secret)
  619. secret.setown(loadLocalSecret(category, name));
  620. if (!secret)
  621. secret.setown(requestVaultSecret(category, nullptr, name, nullptr));
  622. return secret.getClear();
  623. }
  624. extern jlib_decl bool getSecretKeyValue(MemoryBuffer & result, IPropertyTree *secret, const char * key)
  625. {
  626. IPropertyTree *tree = secret->queryPropTree(key);
  627. if (tree)
  628. return tree->getPropBin(nullptr, result);
  629. return false;
  630. }
  631. extern jlib_decl bool getSecretKeyValue(StringBuffer & result, IPropertyTree *secret, const char * key)
  632. {
  633. IPropertyTree *tree = secret->queryPropTree(key);
  634. if (!tree)
  635. return false;
  636. if (tree->isBinary(nullptr))
  637. {
  638. MemoryBuffer mb;
  639. tree->getPropBin(nullptr, mb);
  640. //caller implies it's a string
  641. result.append(mb.length(), mb.toByteArray());
  642. return true;
  643. }
  644. const char *value = tree->queryProp(nullptr);
  645. if (value)
  646. {
  647. result.append(value);
  648. return true;
  649. }
  650. return false;
  651. }
  652. extern jlib_decl bool getSecretValue(StringBuffer & result, const char *category, const char * name, const char * key, bool required)
  653. {
  654. Owned<IPropertyTree> secret = getSecret(category, name);
  655. if (required && !secret)
  656. throw MakeStringException(-1, "secret %s.%s not found", category, name);
  657. bool found = getSecretKeyValue(result, secret, key);
  658. if (required && !found)
  659. throw MakeStringException(-1, "secret %s.%s missing key %s", category, name, key);
  660. return true;
  661. }