Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPCC-29981 Improve gateway support in ESDL script #17642

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

timothyklemm
Copy link
Contributor

  • Add support for gateway URLs that are references to secrets that can be resolved by the query target.
  • Add support for gateway URLs that are references to secrets that can be resolved by the ESP. Only url, username, and password secret properties are used to assemble a replacement URL for the gateway. Each secret must explicitly enable compatibility with this use case.
  • Add support for explicit pass-through values not interpreted by the ESP.
  • Ensure inline URLs can be updated with separately configured username and password values. These updates must be enabled by defining either Gateways/@resolveInlineURLs as true or, for backward compatibility, Gateways/@legacyTransformTarget as a non-empty value.

Type of change:

  • This change is a bug fix (non-breaking change which fixes an issue).
  • This change is a new feature (non-breaking change which adds functionality).
  • This change improves the code (refactor or other change that does not change the functionality)
  • This change fixes warnings (the fix does not alter the functionality or the generated code)
  • This change is a breaking change (fix or feature that will cause existing behavior to change).
  • This change alters the query API (existing queries will have to be recompiled)

Checklist:

  • My code follows the code style of this project.
    • My code does not create any new warnings from compiler, build system, or lint.
  • The commit message is properly formatted and free of typos.
    • The commit message title makes sense in a changelog, by itself.
    • The commit is signed.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly, or...
    • I have created a JIRA ticket to update the documentation.
    • Any new interfaces or exported functions are appropriately commented.
  • I have read the CONTRIBUTORS document.
  • The change has been fully tested:
    • I have added tests to cover my changes.
    • All new and existing tests passed.
    • I have checked that this change does not introduce memory leaks.
    • I have used Valgrind or similar tools to check for potential issues.
  • I have given due consideration to all of the following potential concerns:
    • Scalability
    • Performance
    • Security
    • Thread-safety
    • Cloud-compatibility
    • Premature optimization
    • Existing deployed queries will not be broken
    • This change fixes the problem, not just the symptom
    • The target branch of this pull request is appropriate for such a change.
  • There are no similar instances of the same problem that should be addressed
    • I have addressed them here
    • I have raised JIRA issues to address them separately
  • This is a user interface / front-end modification
    • I have tested my changes in multiple modern browsers
    • The component(s) render as expected

Smoketest:

  • Send notifications about my Pull Request position in Smoketest queue.
  • Test my draft Pull Request.

Testing:

@timothyklemm timothyklemm marked this pull request as draft August 2, 2023 20:20
@github-actions
Copy link

github-actions bot commented Aug 2, 2023

Copy link
Contributor

@asselitx asselitx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there may be a couple small changes needed.

static constexpr const char* gwLocalSecretPrefix = "local-secret:";
static constexpr const size_t gwLocalSecretPrefixLength = strlen(gwLocalSecretPrefix);
static constexpr const char* gwPassThroughPrefix = "pass-through:";
static constexpr const size_t gwPassThroughPrefixLength = strlen(gwPassThroughPrefix);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you haven't already, would be good to open a ticket to document this new behavior so we don't forget to do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The possible existence of Method/Gateways is included in platform documentation. I will wait until the full extent of changes is known before submitting a ticket for the documentation team.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I was thinking just for our developer ESDL scripting docs, but either way.

const char* name = gateway.queryProp("@name");
StringBuffer url(gateway.queryProp("@url"));
if (url.isEmpty())
throw makeStringExceptionV(-1, "gateway %s: missing url property", name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe check to ensure we have a @name - unless that is already previously guaranteed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You commented on @name twice. I'll address here. There is a logical requirement for @name. In the legacy transform, @name becomes @ServiceName (see lines 1477 and 1479). There is a need for something that the roxie can use to select the gateway and we've assumed for years it is called @name.

That's not to say that nothing should change. What should the absence of @name mean? We've been assuming its presence for legacy transforms. Absence here, and at 1414, only affects error reporting. Should we default to something like "unknown" for error reporting, because we don't know there isn't another naming convention in use, but fail when we try to act on it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was only thinking in terms of the local effect of calling makeStringExceptionV with a possible null pointer. If it seems overkill to have more checks then you should leave as-is.

throw makeStringExceptionV(-1, "gateway %s: '%s' does not identify an 'esp' category secret", name, identification);
if (!isEmptyString(permission) && !secret->getPropBool(permission))
throw makeStringExceptionV(-1, "gateway %s: '%s' does not grant '%s' permission", name, identification, permission);
if (!secret->getProp("url", url.clear()) || url.isEmpty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't the url always get cleared by the first clause, resulting in the second clause returning true even if it was originally populated before the first clause?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The url will always get cleared to prevent getProp from appending the secret's defined URL to the configurations secret identification . The second clause is a sanity check that the secret didn't define an empty URL. I think the logic is as intended.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I guess I misunderstood. I thought there could be a valid case here where it wasn't required to resolve the secret.

esp/services/esdl_svc_engine/esdl_binding.cpp Outdated Show resolved Hide resolved
@asselitx asselitx self-requested a review August 8, 2023 19:08
Copy link
Member

@afishbeck afishbeck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timothyklemm generally looks fine. Some comments about naming, syntax, etc.

url.clear();
url.append(scheme);
url.append("://");
if (userinfo.length()>0 )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space. I also think the >0 on many of these ifs is a bit superfluous. if (userinfo.length()) would suffice.

url.appendf("%s@", userinfo.str());
url.append(host);
if (port.length() > 0)
url.appendf(":%s", port.str());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url.append(':').append(port);
might be slightly faster.

throw makeStringExceptionV(-1, "gateway %s: '%s' is not of the form \"[ vault-id ':' ] secret-name\"", name, identification);
}
if (!secret)
throw makeStringExceptionV(-1, "gateway %s: '%s' does not identify an 'esp' category secret", name, identification);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wording might be slightly confusing. Mostly in the sense that I would say the secret wasn't found rather than "doesn't identify". But the rest including mentioning the category is good.

throw makeStringExceptionV(-1, "gateway %s: missing url property", name);
if (strncmp(url, gwLocalSecretPrefix, gwLocalSecretPrefixLength))
throw makeStringExceptionV(-1, "gateway %s: expected '%s...'; got '%s'", name, gwLocalSecretPrefix, url.str());
const char* identification = url.str() + gwLocalSecretPrefixLength;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ECL I currently require connection secrets to start with "http-connect-". We should consider doing the same here. It means the secrets were explictly created for this purpose. I may regret it in the future, who knows, but it gives a little control and forced standardization.

tgtcfg.setown(createPTreeFromIPT(tgtcfg));
const char* qt = tgtcfg->queryProp("@querytype");
bool published = (qt && (strieq(qt, "roxie") || strieq(qt, "wsecl")));
const char* permission = (published ? "allowPublishedGatewayUsage" : "allowUnpublishedGatewayUsage");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because permission is used in so many overloaded ways I wonder if something like the term "requiredUsage" would work instead?

if (!secret->getProp("url", url.clear()) || url.isEmpty())
throw makeStringExceptionV(-1, "gateway %s: '%s' missing required 'url' property; credential-only secrets not supported", name, identification);
const char* username = secret->queryProp("username");
if (isEmptyString(username) && !secret->getPropBool("insecure"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The term insecure is a bit ambiguous. It makes me think of things like do we accept self signed certs or not verify the server.. some of those types of options may be needed for dev secrets in the future.
Can we call it something else?

gateway.setProp("@url", url);
}

void EsdlServiceImpl::adjustURL(StringBuffer& url, const char* username, const char* password) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put something in the method name about adding credentials? adjustURL is a bit ambiguous.

@timothyklemm timothyklemm force-pushed the hpcc-29981-gateway_transformations branch from b572b4a to 724eecb Compare September 19, 2023 19:25
Tim Klemm added 9 commits November 10, 2023 09:52
- Add a transactional secret cache to the script context.
- Add secret support to http-post-xml.
- Update secret support in mysql.

Signed-off-by: Tim Klemm <[email protected]>
- Add support for gateway URLs that are references to secrets that can be
  resolved by the query target.
- Add support for gateway URLs that are references to secrets that can be
  resolved by the ESP. Only url, username, and password secret properties
  are used to assemble a replacement URL for the gateway. Each secret must
  explicitly enable compatibility with this use case.
- Add support for explicit pass-through values not interpreted by the ESP.
- Ensure inline URLs can be updated with separately configured username and
  password values. These updates must be enabled by defining either
  Gateways/@resolveInlineURLs as true or, for backward compatibility,
  Gateways/@legacyTransformTarget as a non-empty value.

Signed-off-by: Tim Klemm <[email protected]>
- Anticipate alternate update requirements, such as for database access.
- Prevent two versions of one secret from being used in one transaction.
@timothyklemm timothyklemm force-pushed the hpcc-29981-gateway_transformations branch from 072f211 to 512cc33 Compare November 16, 2023 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants