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

Adds parser for Docker config. #102

Merged
merged 10 commits into from
Feb 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.

### Fixed

## 0.1.2
### Added
- Use credentials from Docker config if none can be found otherwise ([]())
Copy link
Contributor Author

@coollog coollog Feb 15, 2018

Choose a reason for hiding this comment

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

This should be populated with the next PR that adds this functionality to RetrieveCredentialsStep.


## 0.1.1
### Added
- Simple example `helloworld` project under `examples/` ([#62](https://github.com/google/jib/pull/62))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* the License.
*/

package com.google.cloud.tools.jib.registry;
package com.google.cloud.tools.jib.registry.credentials;

import com.google.cloud.tools.jib.http.Authorization;
import com.google.common.io.Resources;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

import com.google.cloud.tools.jib.Timer;
import com.google.cloud.tools.jib.http.Authorization;
import com.google.cloud.tools.jib.registry.DockerCredentialRetriever;
import com.google.cloud.tools.jib.registry.NonexistentDockerCredentialHelperException;
import com.google.cloud.tools.jib.registry.NonexistentServerUrlDockerCredentialHelperException;
import com.google.cloud.tools.jib.registry.credentials.DockerCredentialRetriever;
import com.google.cloud.tools.jib.registry.credentials.NonexistentDockerCredentialHelperException;
import com.google.cloud.tools.jib.registry.credentials.NonexistentServerUrlDockerCredentialHelperException;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.util.concurrent.Callable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
/** Static initializers for {@link Authorization}. */
public class Authorizations {

/** Creates an {@link Authorization} with a {@code Bearer} token. */
public static Authorization withBearerToken(String token) {
return new Authorization("Bearer", token);
}

/** Creates an {@link Authorization} with a {@code Basic} credentials. */
public static Authorization withBasicCredentials(String username, String secret) {
String credentials = username + ":" + secret;
String token =
Expand All @@ -35,5 +37,10 @@ public static Authorization withBasicCredentials(String username, String secret)
return new Authorization("Basic", token);
}

/** Creates an {@link Authorization} with a base64-encoded {@code username:password} string. */
Copy link
Member

Choose a reason for hiding this comment

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

do we/can we ensure this is only used for docker-config consumed credentials?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is intended to be used with any basic authorization token, which are in the form of username:password.

Copy link
Member

Choose a reason for hiding this comment

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

oh yeah, I didn't really remember if Authorization was part of the api, but I mean are we only creating Authorizations withBasicToken when using docker-config?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, the only place that uses this now is in the DockerConfigCredentialRetriever.

public static Authorization withBasicToken(String token) {
return new Authorization("Basic", token);
}

private Authorizations() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.cloud.tools.jib.registry.credentials;

import com.google.cloud.tools.jib.http.Authorization;
import com.google.cloud.tools.jib.http.Authorizations;
import com.google.cloud.tools.jib.json.JsonTemplateMapper;
import com.google.cloud.tools.jib.registry.credentials.json.DockerConfigTemplate;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.annotation.Nullable;

/**
* Retrieves registry credentials from the Docker config.
*
* <p>The credentials are searched in the following order (stopping when credentials are found):
*
* <ol>
* <li>If there is an {@code auth} defined for a registry.
* <li>Using the {@code credsStore} credential helper, if available.
* <li>Using the credential helper from {@code credHelpers}, if available.
* </ol>
*
* @see <a
* href="https://docs.docker.com/engine/reference/commandline/login/">https://docs.docker.com/engine/reference/commandline/login/</a>
*/
public class DockerConfigCredentialRetriever {

/**
* @see <a
* href="https://docs.docker.com/engine/reference/commandline/login/#privileged-user-requirement">https://docs.docker.com/engine/reference/commandline/login/#privileged-user-requirement</a>
*/
private static final Path DOCKER_CONFIG_FILE =
Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("config.json");

/** Factory class for constructing {@link DockerCredentialRetriever}. */
@VisibleForTesting
static class DockerCredentialRetrieverFactory {

private final String registry;

private DockerCredentialRetrieverFactory(String registry) {
this.registry = registry;
}

@VisibleForTesting
DockerCredentialRetriever withSuffix(String credentialHelperSuffix) {
return new DockerCredentialRetriever(registry, credentialHelperSuffix);
}
}

private final String registry;
private final Path dockerConfigFile;
private final DockerCredentialRetrieverFactory dockerCredentialRetrieverFactory;

public DockerConfigCredentialRetriever(String registry) {
this(registry, DOCKER_CONFIG_FILE);
}

@VisibleForTesting
DockerConfigCredentialRetriever(String registry, Path dockerConfigFile) {
this.registry = registry;
this.dockerConfigFile = dockerConfigFile;
this.dockerCredentialRetrieverFactory = new DockerCredentialRetrieverFactory(registry);
}

@VisibleForTesting
DockerConfigCredentialRetriever(
String registry,
Path dockerConfigFile,
DockerCredentialRetrieverFactory dockerCredentialRetrieverFactory) {
this.registry = registry;
this.dockerConfigFile = dockerConfigFile;
this.dockerCredentialRetrieverFactory = dockerCredentialRetrieverFactory;
}

/** @return {@link Authorization} found for {@code registry}, or {@code null} if not found */
@Nullable
public Authorization retrieve() {
DockerConfigTemplate dockerConfigTemplate = loadDockerConfigTemplate();
if (dockerConfigTemplate == null) {
return null;
}

String auth = dockerConfigTemplate.getAuthFor(registry);
if (auth != null) {
return Authorizations.withBasicToken(auth);
}

String credentialHelperSuffix = dockerConfigTemplate.getCredentialHelperFor(registry);
if (credentialHelperSuffix != null) {
try {
return dockerCredentialRetrieverFactory.withSuffix(credentialHelperSuffix).retrieve();

} catch (IOException
| NonexistentServerUrlDockerCredentialHelperException
| NonexistentDockerCredentialHelperException ex) {
// Ignores credential helper retrieval exceptions.
}
}

return null;
}

/** Loads the Docker config JSON and caches it. */
@Nullable
private DockerConfigTemplate loadDockerConfigTemplate() {
// Loads the Docker config.
if (!Files.exists(dockerConfigFile)) {
return null;
}
try {
return JsonTemplateMapper.readJsonFromFile(dockerConfigFile, DockerConfigTemplate.class);

} catch (IOException ex) {
// TODO: Throw some exception about not being able to parse Docker config.
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* the License.
*/

package com.google.cloud.tools.jib.registry;
package com.google.cloud.tools.jib.registry.credentials;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -27,7 +27,6 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

// TODO: Replace with non-CLI method.
/**
* Retrieves Docker credentials with a Docker credential helper.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* the License.
*/

package com.google.cloud.tools.jib.registry;
package com.google.cloud.tools.jib.registry.credentials;

/** Thrown because the requested credential helper CLI does not exist. */
public class NonexistentDockerCredentialHelperException extends Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* the License.
*/

package com.google.cloud.tools.jib.registry;
package com.google.cloud.tools.jib.registry.credentials;

/** Thrown because the credential helper does not have credentials for the specified server URL. */
public class NonexistentServerUrlDockerCredentialHelperException extends Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
package com.google.cloud.tools.jib.registry.credentials;

import com.google.cloud.tools.jib.http.Authorization;
import com.google.cloud.tools.jib.registry.DockerCredentialRetriever;
import com.google.cloud.tools.jib.registry.NonexistentDockerCredentialHelperException;
import com.google.cloud.tools.jib.registry.NonexistentServerUrlDockerCredentialHelperException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.cloud.tools.jib.registry.credentials.json;

import com.google.cloud.tools.jib.json.JsonTemplate;
import com.google.common.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;

/**
* Template for a Docker config file.
*
* <p>Example:
*
* <pre>{@code
* {
* "auths": {
* "registry": {
* "auth": "username:password in base64"
* },
* "anotherregistry": {},
* ...
* },
* "credsStore": "credential helper name",
* "credHelpers": {
* "registry": "credential helper name",
* "anotherregistry": "another credential helper name",
* ...
* }
* }
* }</pre>
*
* If an {@code auth} is defined for a registry, that is a valid {@code Basic} authorization to use
* for that registry.
*
* <p>If {@code credsStore} is defined, is a credential helper that stores authorizations for all
* registries listed under {@code auths}.
*
* <p>Each entry in {@code credHelpers} is a mapping from a registry to a credential helper that
* stores the authorization for that registry.
*
* @see <a
* href="https://www.projectatomic.io/blog/2016/03/docker-credentials-store/">https://www.projectatomic.io/blog/2016/03/docker-credentials-store/</a>
*/
public class DockerConfigTemplate extends JsonTemplate {

/** Template for an {@code auth} defined for a registry under {@code auths}. */
private static class AuthTemplate extends JsonTemplate {

private String auth;
}

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

private String credsStore;

/** Maps from registry to credential helper name. */
private final Map<String, String> credHelpers = new HashMap<>();

/**
* @return the base64-encoded {@code Basic} authorization for {@code registry}, or {@code null} if
* none exists
*/
@Nullable
public String getAuthFor(String registry) {
if (!auths.containsKey(registry)) {
return null;
}
return auths.get(registry).auth;
}

/**
* @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 && auths.containsKey(registry)) {
return credsStore;
}
if (credHelpers.containsKey(registry)) {
return credHelpers.get(registry);
}
return null;
}

@VisibleForTesting
DockerConfigTemplate addAuth(String registry, @Nullable String auth) {
AuthTemplate authTemplate = new AuthTemplate();
authTemplate.auth = auth;
auths.put(registry, authTemplate);
return this;
}

@VisibleForTesting
DockerConfigTemplate setCredsStore(String credsStore) {
this.credsStore = credsStore;
return this;
}

@VisibleForTesting
DockerConfigTemplate addCredHelper(String registry, String credHelper) {
credHelpers.put(registry, credHelper);
return this;
}
}
Loading