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

Encode JSON string in Pulsar auth params #40493

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -137,14 +137,25 @@ private String getAuthenticationParamsJson(Map<String, String> params) {
try {
return sortedParams.entrySet()
.stream()
.map((entry) -> "\"%s\":\"%s\"".formatted(entry.getKey(), entry.getValue()))
.map((entry) -> "\"%s\":\"%s\"".formatted(entry.getKey(), escapeJson(entry.getValue())))
.collect(Collectors.joining(",", "{", "}"));
}
catch (Exception ex) {
throw new IllegalStateException("Could not convert auth parameters to encoded string", ex);
}
}

private String escapeJson(String raw) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we do not directly bring in a JSON library (e.g. Gson or Jackson) we can not use the simple facilities that these libs have to "encode a JSON string".

Here were the options:

Use JSON lib

Add required dependency on Jackson or Gson and use their built-in methods

  • ✅ solid encode impl maintained by JSON lib
  • ❌ ❌ ❌ have to bring in direct dep on JSON lib and this forces it upon our users (not an option)

Add feature to Spring Boot JsonParser

Add "encode a JSON string" to our JsonParser abstraction

  • ✅ can be used in other areas of Spring Boot codebase
  • ❌ created and maintained by us (more work)
  • ❌ impl no more solid than if it were a private impl inside the mapper
  • ❌ nowhere else in the Boot codebase needs this AFAIK

Hand-roll basic encoder

Inline a simple encode JSON facility in the mapper.

  • ❌ basic impl maintained by us
  • ❌ ✅ not a perfect solution but will solve the majority of the cases
  • ✅ ✅ no direct dep on JSON lib
  • ✅ private impl localized to the mapper

return raw.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("/", "\\/")
.replace("\b", "\\b")
.replace("\t", "\\t")
.replace("\n", "\\n")
.replace("\f", "\\f")
.replace("\r", "\\r");
}

<T> void customizeProducerBuilder(ProducerBuilder<T> producerBuilder) {
PulsarProperties.Producer properties = this.properties.getProducer();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
Expand Down
Expand Up @@ -81,8 +81,10 @@ void customizeClientBuilderWhenHasNoAuthentication() {
@Test
void customizeClientBuilderWhenHasAuthentication() throws UnsupportedAuthenticationException {
PulsarProperties properties = new PulsarProperties();
Map<String, String> params = Map.of("param", "name");
String authParamString = "{\"param\":\"name\"}";
Map<String, String> params = Map.of("simpleParam", "foo", "complexParam",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are not exhausting all possible replacements characters but are covering the majority.

@philwebb should we add a bit more coverage here?

"{\n\t\"k1\" : \"v1\",\n\t\"k2\":\"v2\"\n}");
String authParamString = "{\"complexParam\":\"{\\n\\t\\\"k1\\\" : \\\"v1\\\",\\n\\t\\\"k2\\\":\\\"v2\\\"\\n}\""
+ ",\"simpleParam\":\"foo\"}";
properties.getClient().getAuthentication().setPluginClassName("myclass");
properties.getClient().getAuthentication().setParam(params);
ClientBuilder builder = mock(ClientBuilder.class);
Expand Down Expand Up @@ -166,8 +168,10 @@ void customizeAdminBuilderWhenHasNoAuthentication() {
@Test
void customizeAdminBuilderWhenHasAuthentication() throws UnsupportedAuthenticationException {
PulsarProperties properties = new PulsarProperties();
Map<String, String> params = Map.of("param", "name");
String authParamString = "{\"param\":\"name\"}";
Map<String, String> params = Map.of("simpleParam", "foo", "complexParam",
"{\n\t\"k1\" : \"v1\",\n\t\"k2\":\"v2\"\n}");
String authParamString = "{\"complexParam\":\"{\\n\\t\\\"k1\\\" : \\\"v1\\\",\\n\\t\\\"k2\\\":\\\"v2\\\"\\n}\""
+ ",\"simpleParam\":\"foo\"}";
properties.getAdmin().getAuthentication().setPluginClassName("myclass");
properties.getAdmin().getAuthentication().setParam(params);
PulsarAdminBuilder builder = mock(PulsarAdminBuilder.class);
Expand Down