diff --git a/esp/services/esdl_svc_engine/esdl_binding.cpp b/esp/services/esdl_svc_engine/esdl_binding.cpp index 21b82a8bde7..e31d00007e2 100755 --- a/esp/services/esdl_svc_engine/esdl_binding.cpp +++ b/esp/services/esdl_svc_engine/esdl_binding.cpp @@ -1394,7 +1394,7 @@ EsdlServiceImpl::IUpdatableGateway* EsdlServiceImpl::createInlineGateway(const I return new CLegacyUrlGateway(gw, gwName, gwUrl); } -void EsdlServiceImpl::applyGatewayUpdates(IPTreeIterator& gwIt, const UpdatableGateways& updatables, GatewayUpdaters& updaters, TransactionSecrets& secrets) const +void EsdlServiceImpl::applyGatewayUpdates(IPTreeIterator& gwIt, const UpdatableGateways& updatables, GatewayUpdaters& updaters, IEsdlScriptContext* scriptContext) const { ForEach(gwIt) { @@ -1405,9 +1405,9 @@ void EsdlServiceImpl::applyGatewayUpdates(IPTreeIterator& gwIt, const UpdatableG UpdatableGateways::const_iterator ugIt = updatables.find(name); if (updatables.end() == ugIt) continue; - Owned updater(ugIt->second->getUpdater(updaters, secrets)); + Owned updater(ugIt->second->getUpdater(updaters, scriptContext)); if (updater) - updater->updateGateway(gw, "allowPublishedGatewayUsage"); + updater->updateGateway(gw); } } @@ -1799,7 +1799,7 @@ void EsdlServiceImpl::prepareFinalRequest(IEspContext &context, if (ctxGateways) { gwIt.setown(ctxGateways->getElements("Gateway")); - applyGatewayUpdates(*gwIt, mgcIt->second.targetContext, updaters, secrets); + applyGatewayUpdates(*gwIt, mgcIt->second.targetContext, updaters, scriptContext); } } toXML(tgtctx.get(), reqProcessed); @@ -1822,7 +1822,7 @@ void EsdlServiceImpl::prepareFinalRequest(IEspContext &context, // used in subsequent transactions. Owned copy(createPTreeFromIPT(cfgGateways)); gwIt.setown(copy->getElements("Gateway")); - applyGatewayUpdates(*gwIt, mgcIt->second.legacyTransform, updaters, secrets); + applyGatewayUpdates(*gwIt, mgcIt->second.legacyTransform, updaters, scriptContext); } else gwIt.setown(cfgGateways->getElements("Gateway")); @@ -1905,51 +1905,22 @@ EsdlServiceImpl::~EsdlServiceImpl() } } -IPTree* EsdlServiceImpl::TransactionSecrets::getVaultSecret(const char* category, const char* vaultId, const char* name) -{ - Owned secret(lookup(category, vaultId, name)); - if (!secret) - { - secret.setown(::getVaultSecret(category, vaultId, name)); - if (secret) - store(*secret, category, vaultId, name); - } - return secret.getClear(); -} - -IPTree* EsdlServiceImpl::TransactionSecrets::getSecret(const char* category, const char* name) -{ - Owned secret(lookup(category, "", name)); - if (!secret) - { - secret.setown(::getVaultSecret(category, "", name)); - if (secret) - store(*secret, category, "", name); - } - return secret; -} - -IPTree* EsdlServiceImpl::TransactionSecrets::lookup(const char* category, const char* vaultId, const char* name) const +EsdlServiceImpl::IGatewayUpdater* EsdlServiceImpl::CUpdatableGateway::getUpdater(GatewayUpdaters& updaters, TransactionSecrets& secrets) const { - Key key = std::make_tuple(category ? category : "", vaultId ? vaultId : "", name ? name : ""); - Cache::const_iterator it = cache.find(key); - if (it != cache.end()) + GatewayUpdaters::iterator it = updaters.find(updatersKey); + if (it != updaters.end()) return it->second.getLink(); - return nullptr; -} - -void EsdlServiceImpl::TransactionSecrets::store(IPTree& secret, const char* category, const char* vaultId, const char* name) -{ - Key key = std::make_tuple(category ? category : "", vaultId ? vaultId : "", name ? name : ""); - cache[key].set(&secret); + Owned spawned(getUpdater(secrets)); + updaters[updatersKey].setown(spawned.getLink()); + return spawned.getClear(); } -EsdlServiceImpl::IGatewayUpdater* EsdlServiceImpl::CUpdatableGateway::getUpdater(GatewayUpdaters& updaters, TransactionSecrets& secrets) const +EsdlServiceImpl::IGatewayUpdater* EsdlServiceImpl::CUpdatableGateway::getUpdater(GatewayUpdaters& updaters, IEsdlScriptContext* scriptContext) const { GatewayUpdaters::iterator it = updaters.find(updatersKey); if (it != updaters.end()) return it->second.getLink(); - Owned spawned(getUpdater(secrets)); + Owned spawned(getUpdater(scriptContext)); updaters[updatersKey].setown(spawned.getLink()); return spawned.getClear(); } @@ -1997,7 +1968,12 @@ EsdlServiceImpl::CLegacyUrlGateway* EsdlServiceImpl::CLegacyUrlGateway::getUpdat return LINK(const_cast(this)); } -void EsdlServiceImpl::CLegacyUrlGateway::updateGateway(IPTree& gw, const char*) +EsdlServiceImpl::CLegacyUrlGateway* EsdlServiceImpl::CLegacyUrlGateway::getUpdater(IEsdlScriptContext*) const +{ + return LINK(const_cast(this)); +} + +void EsdlServiceImpl::CLegacyUrlGateway::updateGateway(IPTree& gw) { gw.setProp("@url", url); } @@ -2019,80 +1995,26 @@ EsdlServiceImpl::CLegacyUrlGateway::CLegacyUrlGateway(const IPTree& gw, const ch updateURLCredentials(url, username, password); } -void EsdlServiceImpl::CLocalSecretGateway::CUpdater::updateGateway(IPTree& gw, const char* requiredUsage) -{ - if (!secret) - throw makeStringExceptionV(-1, "gateway %s: 'esp' category secret '%s' not found", "?", entry->secretId.str()); - entry->doUpdate(gw, *secret, requiredUsage); -} - -EsdlServiceImpl::CLocalSecretGateway::CUpdater::CUpdater(const CLocalSecretGateway& _entry, TransactionSecrets& secrets) -{ - entry.set(&_entry); - if (entry->vaultId.isEmpty()) - secret.setown(secrets.getSecret("esp", entry->secretName)); - else - secret.setown(secrets.getVaultSecret("esp", entry->vaultId.str(), entry->secretName)); -} - -EsdlServiceImpl::IGatewayUpdater* EsdlServiceImpl::CLocalSecretGateway::getUpdater(TransactionSecrets& secrets) const -{ - return new CUpdater(*this, secrets); -} - -EsdlServiceImpl::CLocalSecretGateway::CLocalSecretGateway(const IPTree& gw, const char* gwName, const char* gwUrl, const char* classPrefix) - : CUpdatableGateway(gwName) -{ - secretId.append(gwUrl + gwLocalSecretPrefixLength); - StringArray tokens; - tokens.appendList(secretId, ":", true); - const char* vaultToken = nullptr; - const char* secretToken = nullptr; - switch (tokens.ordinality()) - { - case 1: // secret name without vault name - secretToken = tokens.item(0); - break; - case 2: // secret name after vault name - vaultToken = tokens.item(0); - secretToken = tokens.item(1); - break; - default: - throw makeStringExceptionV(-1, "gateway %s: '%s' is not of the form \"[ vault-id ':' ] secret-name\"", gwName, secretId.str()); - } - - if (!isEmptyString(vaultToken)) - vaultId.set(vaultToken); - secretName.append(classPrefix).append(secretToken); -} - -void EsdlServiceImpl::CLocalSecretGateway::doUpdate(IPTree& gw, const IPTree& secret, const char* requiredUsage) const -{ - if (!isEmptyString(requiredUsage) && !secret.getPropBool(requiredUsage)) - throw makeStringExceptionV(-1, "gateway %s: secret '%s' does not allow '%s' usage", updatersKey.c_str(), secretId.str(), requiredUsage); -} - -void EsdlServiceImpl::CHttpConnectGateway::doUpdate(IPTree& gw, const IPTree& secret, const char* requiredUsage) const +void EsdlServiceImpl::CHttpConnectGateway::doUpdate(IPTree& gw, const IPTree& secret) const { - CLocalSecretGateway::doUpdate(gw, secret, requiredUsage); StringBuffer url; if (!secret.getProp("url", url.clear()) || url.isEmpty()) - throw makeStringExceptionV(-1, "gateway %s: secret '%s' missing required 'url' property; credential-only secrets not supported", gw.queryProp("@name"), secretId.str()); + throw makeStringExceptionV(-1, "gateway %s: secret '%s' missing required 'url' property; credential-only secrets not supported", gw.queryProp("@name"), identifier.str()); const char* username = secret.queryProp("username"); bool omitCredentials = secret.getPropBool("omitCredentials"); if (isEmptyString(username) && !omitCredentials) - throw makeStringExceptionV(-1, "gateway %s: secret '%s' missing expected 'username' property; set 'omitCredentials` property to 'true' if credentials are not required", gw.queryProp("@name"), secretId.str()); + throw makeStringExceptionV(-1, "gateway %s: secret '%s' missing expected 'username' property; set 'omitCredentials` property to 'true' if credentials are not required", gw.queryProp("@name"), identifier.str()); if (!isEmptyString(username) && omitCredentials) - throw makeStringExceptionV(-1, "gateway %s: secret '%s' contains 'username' and sets 'omitCredentials'", gw.queryProp("@name"), secretId.str()); + throw makeStringExceptionV(-1, "gateway %s: secret '%s' contains 'username' and sets 'omitCredentials'", gw.queryProp("@name"), identifier.str()); const char* password = secret.queryProp("password"); if (isEmptyString(username) && password) - throw makeStringExceptionV(-1, "gateway %s: '%s' invalid use of password without username", gw.queryProp("@name"), secretId.str()); + throw makeStringExceptionV(-1, "gateway %s: '%s' invalid use of password without username", gw.queryProp("@name"), identifier.str()); updateURLCredentials(url, username, password); gw.setProp("@url", url); } EsdlServiceImpl::CHttpConnectGateway::CHttpConnectGateway(const IPTree& gw, const char* gwName, const char* gwUrl) - : CLocalSecretGateway(gw, gwName, gwUrl, "http-connect-") + : TLocalSecretGateway(gw, gwName, gwUrl) { } diff --git a/esp/services/esdl_svc_engine/esdl_binding.hpp b/esp/services/esdl_svc_engine/esdl_binding.hpp index 68fdda9ab40..5b7ac65d52e 100755 --- a/esp/services/esdl_svc_engine/esdl_binding.hpp +++ b/esp/services/esdl_svc_engine/esdl_binding.hpp @@ -72,34 +72,6 @@ typedef int (*cpp_service_method_t)(const char* CtxXML, const char* ReqXML, Stri class EsdlServiceImpl : public CInterface, implements IEspService { -private: - /** - * @brief Cache of secrets accessed as part of transaction processing. - * - * In the context of a single transaction, multiple requests for the same secret should always - * return the same secret data for each request. Cached data does not expire and there is no - * option to reload data. - * - * TODO: Refactor to support additional use cases - * - Provide ESDL script operation support so mysql (now) and http-post-xml (eventually) - * can benefit secret reuse. - * - Encapsulate non-jsecrets access to support non-standard secret sources, unless - * jsecrets is changed to provide this encapsulation. - */ - class TransactionSecrets - { - private: - using Key = std::tuple; - using Cache = std::map>; - Cache cache; - public: - IPTree* getVaultSecret(const char* category, const char* vaultId, const char* name); - IPTree* getSecret(const char* category, const char* name); - protected: - IPTree* lookup(const char* category, const char* vaultId, const char* name) const; - void store(IPTree& secret, const char* category, const char* vaultId, const char* name); - }; - private: /** * @brief Abstraction of a per-transaction gateway updater. @@ -115,7 +87,7 @@ class EsdlServiceImpl : public CInterface, implements IEspService */ interface IGatewayUpdater : public IInterface { - virtual void updateGateway(IPTree& gw, const char* requiredUsage) = 0; + virtual void updateGateway(IPTree& gw) = 0; }; using GatewayUpdaters = std::map>; @@ -143,6 +115,7 @@ class EsdlServiceImpl : public CInterface, implements IEspService interface IUpdatableGateway : public IInterface { virtual IGatewayUpdater* getUpdater(GatewayUpdaters& updaters, TransactionSecrets& secrets) const = 0; + virtual IGatewayUpdater* getUpdater(GatewayUpdaters& updaters, IEsdlScriptContext* scriptContext) const = 0; }; using UpdatableGateways = std::map>; @@ -164,11 +137,13 @@ class EsdlServiceImpl : public CInterface, implements IEspService { public: virtual IGatewayUpdater* getUpdater(GatewayUpdaters& updaters, TransactionSecrets& secrets) const override; + virtual IGatewayUpdater* getUpdater(GatewayUpdaters& updaters, IEsdlScriptContext* scriptContext) const override; protected: std::string updatersKey; protected: CUpdatableGateway(const char* gwName); virtual IGatewayUpdater* getUpdater(TransactionSecrets& secrets) const = 0; + virtual IGatewayUpdater* getUpdater(IEsdlScriptContext* scriptContext) const = 0; bool updateURLCredentials(StringBuffer& url, const char* username, const char* password) const; }; @@ -189,8 +164,9 @@ class EsdlServiceImpl : public CInterface, implements IEspService { protected: // CUpdatableGateway virtual CLegacyUrlGateway* getUpdater(TransactionSecrets&) const override; + virtual CLegacyUrlGateway* getUpdater(IEsdlScriptContext*) const override; public: // IGatewayUpdater - virtual void updateGateway(IPTree& gw, const char*) override; + virtual void updateGateway(IPTree& gw) override; protected: StringBuffer url; public: @@ -202,47 +178,74 @@ class EsdlServiceImpl : public CInterface, implements IEspService * @brief Concrete extension of `CUpdatableGateway` for handling local secrets that is always * extended with secret usage-specific logic. * - * - Ensures `Gateway/@url` matches the pattern `"local-secret:" [ vault-id ":" ] secret-name".` + * - Ensures `Gateway/@url` matches the pattern `"local-secret:" [ vault-id "::" ] secret-name [ "::" version ]`. + * - `secret-name` is required always + * - `vault-id` is required before `version` can be specified * - Defines a standard updater separating dynamically changing secrets from the configuration. - * - Enforces a requirement for a local secret to explicitly allow its data to be shared with - * a backend roxie service. * * Support is provided as an alternative to inline definitions when secret names cannot be * passed to the target roxie for resolution. Use is discouraged unless it is unavoidable. */ - class CLocalSecretGateway : public CUpdatableGateway + template + class TLocalSecretGateway : public CUpdatableGateway { protected: - class CUpdater : public CInterfaceOf + template + class TUpdater : public CInterfaceOf { - friend class CLocalSecretGateway; + friend class TLocalSecretGateway; + static constexpr const char* category = "espUser"; public: // IGatewayUpdater - virtual void updateGateway(IPTree& gw, const char* requiredUsage) override; + virtual void updateGateway(IPTree& gw) override + { + if (!secret) + throw makeStringExceptionV(-1, "gateway %s: '%s' category secret '%s' not found", "?", entry->identifier.str(), category); + entry->doUpdate(gw, *secret); + } protected: - Linked entry; + Linked> entry; Owned secret; public: - CUpdater(const CLocalSecretGateway& _entry, TransactionSecrets& secrets); + TUpdater(const TLocalSecretGateway& _entry, secret_source_t& secrets) + { + entry.set(&_entry); + secret.setown(secrets.getSecret(category, entry->identity)); + } }; protected: // CUpdatableGateway - virtual IGatewayUpdater* getUpdater(TransactionSecrets& secrets) const override; + virtual IGatewayUpdater* getUpdater(TransactionSecrets& secrets) const override + { + return new TUpdater(*this, secrets); + } + virtual IGatewayUpdater* getUpdater(IEsdlScriptContext* scriptContext) const override + { + if (!scriptContext) + return nullptr; + return new TUpdater(*this, *scriptContext); + } protected: - StringBuffer secretId; - StringAttr vaultId; - StringBuffer secretName; + StringBuffer identifier; + secret_identity_t identity; protected: - CLocalSecretGateway(const IPTree& gw, const char* gwName, const char* gwUrl, const char* classPrefix); + TLocalSecretGateway(const IPTree& gw, const char* gwName, const char* gwUrl) + : CUpdatableGateway(gwName) + , identifier(gwUrl + gwLocalSecretPrefixLength) + , identity(identifier) + { + } protected: - virtual void doUpdate(IPTree& gw, const IPTree& secret, const char* requiredUsage) const; + virtual void doUpdate(IPTree& gw, const IPTree& secret) const + { + } }; /** - * @brief Concete extension of `CLocalSecretGateway` to construct a new `Gateway/@url` value + * @brief Concete extension of `TLocalSecretGateway<>` to construct a new `Gateway/@url` value * from secret-defined values. * * - A secret name must begin with "http-connect-". Configurations should omit this prefix, * but its presence is acceptable. - * - A secret must satisfy the requirements defined in `CLocalSecretGateway`. + * - A secret must satisfy the requirements defined in `TLocalSecretGateway<>`. * - A secret must define a non-empty `url` property. * - A secret must define either a non-empty `username` property or set the `omitCredentials` * property to true. @@ -250,10 +253,10 @@ class EsdlServiceImpl : public CInterface, implements IEspService * - A secret must not define `username` and set `omitCredentials` to true. * - A secret must not define a `password` property if `username` is not defined. */ - class CHttpConnectGateway : public CLocalSecretGateway + class CHttpConnectGateway : public TLocalSecretGateway { - protected: // CLocalSecretGateway - void doUpdate(IPTree& gw, const IPTree& secret, const char* requiredUsage) const override; + protected: // TLocalSecretGateway + void doUpdate(IPTree& gw, const IPTree& secret) const override; public: CHttpConnectGateway(const IPTree& gw, const char* gwName, const char* gwUrl); }; @@ -489,7 +492,7 @@ class EsdlServiceImpl : public CInterface, implements IEspService * @param updaters cache of updaters used in the current transaction * @param secrets cache of secrets used in the current transaction */ - void applyGatewayUpdates(IPTreeIterator& gwIt, const UpdatableGateways& updatables, GatewayUpdaters& updaters, TransactionSecrets& secrets) const; + void applyGatewayUpdates(IPTreeIterator& gwIt, const UpdatableGateways& updatables, GatewayUpdaters& updaters, IEsdlScriptContext* scriptContext) const; /** * @brief Implementation of legacy gateway transformation invoked only during preparation of