Skip to content

Commit

Permalink
Look up cred helper additionally with possible suffixes (#650)
Browse files Browse the repository at this point in the history
  • Loading branch information
chanseokoh authored Jul 18, 2018
1 parent fc35488 commit 5c94263
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ private static class AuthTemplate implements JsonTemplate {
@Nullable private String auth;
}

/**
* Returns the first value matching the given key predicates (short-circuiting in the order of
* predicates).
*/
private static <K, T> T findFirstInMapByKey(Map<K, T> map, List<Predicate<K>> keyMatches) {
return keyMatches
.stream()
.map(keyMatch -> findFirstInMapByKey(map, keyMatch))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}

/** Returns the first value matching the given key predicate. */
private static <K, T> T findFirstInMapByKey(Map<K, T> map, Predicate<K> keyMatch) {
return map.keySet().stream().filter(keyMatch).map(map::get).findFirst().orElse(null);
}

/** Maps from registry to its {@link AuthTemplate}. */
private final Map<String, AuthTemplate> auths = new HashMap<>();

Expand All @@ -87,8 +105,8 @@ private static class AuthTemplate implements JsonTemplate {
* <ol>
* <li>Exact registry name
* <li>https:// + registry name
* <li>registry name + arbitrary suffix
* <li>https:// + registry name + arbitrary suffix
* <li>registry name + / + arbitrary suffix
* <li>https:// + registry name + / arbitrary suffix
* </ol>
*
* @param registry the registry to get the authorization for
Expand All @@ -97,48 +115,41 @@ private static class AuthTemplate implements JsonTemplate {
*/
@Nullable
public String getAuthFor(String registry) {
Predicate<String> exactMatch = registry::equals;
Predicate<String> withHttps = ("https://" + registry)::equals;
Predicate<String> startsWith = name -> name.startsWith(registry);
Predicate<String> startsWithAndWithHttps = name -> name.startsWith("https://" + registry);
AuthTemplate authTemplate =
getAuthTemplate(Arrays.asList(exactMatch, withHttps, startsWith, startsWithAndWithHttps));

AuthTemplate authTemplate = findFirstInMapByKey(auths, getRegistryMatchersFor(registry));
return authTemplate != null ? authTemplate.auth : null;
}

/** Returns the first {@link AuthTemplate} matching the given predicates (short-circuiting). */
private AuthTemplate getAuthTemplate(List<Predicate<String>> registryMatches) {
return registryMatches
.stream()
.map(this::getAuthTemplate)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}

/** Returns {@link AuthTemplate} matching the given predicate. */
private AuthTemplate getAuthTemplate(Predicate<String> registryMatch) {
return auths.keySet().stream().filter(registryMatch).map(auths::get).findFirst().orElse(null);
}

/**
* Returns {@code credsStore} or {@code credHelpers} for the given {@code registry}. If there
* exists a matching registry entry in {@code auths}, returns {@code credStore}; otherwise, a
* matching entry in {@code credHelpers} is returned based on the following lookup order:
*
* <ol>
* <li>Exact registry name
* <li>https:// + registry name
* <li>registry name + / + arbitrary suffix
* <li>https:// + registry name + / + arbitrary suffix
* </ol>
*
* @param registry the registry to get the credential helpers for
* @return {@code credsStore} if {@code registry} is present in {@code auths}; otherwise, searches
* {@code credHelpers}; otherwise, {@code null} if not found
*/
@Nullable
public String getCredentialHelperFor(String registry) {
if (credsStore != null) {
// The registry could be prefixed with the HTTPS protocol.
if (auths.containsKey(registry) || auths.containsKey("https://" + registry)) {
return credsStore;
}
}
if (credHelpers.containsKey(registry)) {
return credHelpers.get(registry);
List<Predicate<String>> registryMatchers = getRegistryMatchersFor(registry);
if (credsStore != null && findFirstInMapByKey(auths, registryMatchers) != null) {
return credsStore;
}
return null;
return findFirstInMapByKey(credHelpers, registryMatchers);
}

private List<Predicate<String>> getRegistryMatchersFor(String registry) {
Predicate<String> exactMatch = registry::equals;
Predicate<String> withHttps = ("https://" + registry)::equals;
Predicate<String> withSuffix = name -> name.startsWith(registry + "/");
Predicate<String> WithHttpsAndSuffix = name -> name.startsWith("https://" + registry + "/");
return Arrays.asList(exactMatch, withHttps, withSuffix, WithHttpsAndSuffix);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,71 @@ public void testGetAuthFor_orderOfMatchPreference() throws URISyntaxException, I
"dull-registry: starting with name and with https",
dockerConfig.getAuthFor("dull-registry"));
}

@Test
public void testGetAuthFor_correctSuffixMatching() throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig_extra_matches.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertNull(dockerConfig.getAuthFor("example"));
}

@Test
public void testGetCredentialHelperFor() throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertEquals(
"some credential store", dockerConfig.getCredentialHelperFor("just registry"));
}

@Test
public void testGetCredentialHelperFor_withHttps() throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertEquals(
"some credential store", dockerConfig.getCredentialHelperFor("with.protocol"));
}

@Test
public void testGetCredentialHelperFor_withSuffix() throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertEquals(
"some credential store", dockerConfig.getCredentialHelperFor("with.suffix"));
}

@Test
public void testGetCredentialHelperFor_withProtocolAndSuffix()
throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertEquals(
"some credential store", dockerConfig.getCredentialHelperFor("with.protocol.and.suffix"));
}

@Test
public void testGetCredentialHelperFor_correctSuffixMatching()
throws URISyntaxException, IOException {
Path json = Paths.get(Resources.getResource("json/dockerconfig.json").toURI());

DockerConfigTemplate dockerConfig =
JsonTemplateMapper.readJsonFromFile(json, DockerConfigTemplate.class);

Assert.assertNull(dockerConfig.getCredentialHelperFor("example"));
Assert.assertNull(dockerConfig.getCredentialHelperFor("another.example"));
}
}
17 changes: 15 additions & 2 deletions jib-core/src/test/resources/json/dockerconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@
"some other registry":{"auth":"some other auth"},
"some registry":{"auth":"some auth","password":"ignored"},
"https://registry":{"auth":"token"},

"just registry":{},
"https://with.protocol":{}
"https://with.protocol":{},
"with.suffix/suffix/":{},
"https://with.protocol.and.suffix/v10/":{},

"example.com":{"auth":"should not match example"}
},
"credsStore":"some credential store",
"credHelpers":{
"another registry":"another credential helper",
"some registry":"some credential helper",
"index.docker.io":"index.docker.io credential helper"
"index.docker.io":"index.docker.io credential helper",

"just.registry.in.helpers":"credHelper for just.registry.in.helpers",
"https://with.protocol.in.helpers":"credHelper for https://with.protocol.in.helpers",
"with.suffix.in.helpers/v2/":"credHelper for with.suffix.in.helpers/v2/",
"https://with.protocol.and.suffix.in.helpers/suffix":
"credHelper for https://with.protocol.and.suffix.in.helpers/suffix",

"another.example.com.in.helpers":"should not match example"
}
}

0 comments on commit 5c94263

Please sign in to comment.