Skip to content

Commit eeeabc9

Browse files
committed
Add support for .dockercfg files to handle auth for push command
1 parent ffbbe3d commit eeeabc9

18 files changed

+502
-28
lines changed

src/main/java/com/github/dockerjava/api/model/AuthConfig.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.dockerjava.api.model;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonProperty;
45

56
public class AuthConfig {
@@ -23,6 +24,8 @@ public class AuthConfig {
2324
@JsonProperty("serveraddress")
2425
private String serverAddress = DEFAULT_SERVER_ADDRESS;
2526

27+
private String auth;
28+
2629
public String getUsername() {
2730
return username;
2831
}
@@ -55,6 +58,16 @@ public void setServerAddress(String serverAddress) {
5558
this.serverAddress = serverAddress;
5659
}
5760

61+
@JsonIgnore
62+
public String getAuth() {
63+
return auth;
64+
}
65+
66+
@JsonProperty("auth")
67+
public void setAuth(String auth) {
68+
this.auth = auth;
69+
}
70+
5871
@Override
5972
public String toString() {
6073
return "AuthConfig{" +
@@ -64,4 +77,53 @@ public String toString() {
6477
", serverAddress='" + serverAddress + '\'' +
6578
'}';
6679
}
80+
81+
@Override
82+
public int hashCode() {
83+
final int prime = 31;
84+
int result = 1;
85+
result = prime * result + ((auth == null) ? 0 : auth.hashCode());
86+
result = prime * result + ((email == null) ? 0 : email.hashCode());
87+
result = prime * result + ((password == null) ? 0 : password.hashCode());
88+
result = prime * result + ((serverAddress == null) ? 0 : serverAddress.hashCode());
89+
result = prime * result + ((username == null) ? 0 : username.hashCode());
90+
return result;
91+
}
92+
93+
@Override
94+
public boolean equals(Object obj) {
95+
if (this == obj)
96+
return true;
97+
if (obj == null)
98+
return false;
99+
if (getClass() != obj.getClass())
100+
return false;
101+
AuthConfig other = (AuthConfig) obj;
102+
if (auth == null) {
103+
if (other.auth != null)
104+
return false;
105+
} else if (!auth.equals(other.auth))
106+
return false;
107+
if (email == null) {
108+
if (other.email != null)
109+
return false;
110+
} else if (!email.equals(other.email))
111+
return false;
112+
if (password == null) {
113+
if (other.password != null)
114+
return false;
115+
} else if (!password.equals(other.password))
116+
return false;
117+
if (serverAddress == null) {
118+
if (other.serverAddress != null)
119+
return false;
120+
} else if (!serverAddress.equals(other.serverAddress))
121+
return false;
122+
if (username == null) {
123+
if (other.username != null)
124+
return false;
125+
} else if (!username.equals(other.username))
126+
return false;
127+
return true;
128+
}
67129
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.github.dockerjava.core;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.nio.charset.Charset;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
import org.apache.commons.codec.binary.Base64;
11+
import org.apache.commons.io.FileUtils;
12+
import org.apache.commons.lang.StringUtils;
13+
14+
import com.fasterxml.jackson.core.type.TypeReference;
15+
import com.fasterxml.jackson.databind.ObjectMapper;
16+
import com.github.dockerjava.api.model.AuthConfig;
17+
18+
public class AuthConfigFile {
19+
private static final ObjectMapper MAPPER = new ObjectMapper();
20+
private static final TypeReference<Map<String, AuthConfig>> CONFIG_MAP_TYPE = new TypeReference<Map<String, AuthConfig>>() {};
21+
private final Map<String, AuthConfig> authConfigMap;
22+
23+
public AuthConfigFile() {
24+
authConfigMap = new HashMap<String, AuthConfig>();
25+
}
26+
27+
void addConfig(AuthConfig config) {
28+
authConfigMap.put(config.getServerAddress(), config);
29+
}
30+
31+
public AuthConfig resolveAuthConfig(String hostname) {
32+
if (StringUtils.isEmpty(hostname) || AuthConfig.DEFAULT_SERVER_ADDRESS.equals(hostname)) {
33+
return authConfigMap.get(AuthConfig.DEFAULT_SERVER_ADDRESS);
34+
}
35+
AuthConfig c = authConfigMap.get(hostname);
36+
if (c != null) {
37+
return c;
38+
}
39+
40+
// Maybe they have a legacy config file, we will iterate the keys converting
41+
// them to the new format and testing
42+
String normalizedHostname = convertToHostname(hostname);
43+
for (Map.Entry<String, AuthConfig> entry : authConfigMap.entrySet()) {
44+
String registry = entry.getKey();
45+
AuthConfig config = entry.getValue();
46+
if (convertToHostname(registry).equals(normalizedHostname)) {
47+
return config;
48+
}
49+
}
50+
return null;
51+
}
52+
53+
@Override
54+
public int hashCode() {
55+
final int prime = 31;
56+
int result = 1;
57+
result = prime * result + ((authConfigMap == null) ? 0 : authConfigMap.hashCode());
58+
return result;
59+
}
60+
61+
@Override
62+
public boolean equals(Object obj) {
63+
if (this == obj)
64+
return true;
65+
if (obj == null)
66+
return false;
67+
if (getClass() != obj.getClass())
68+
return false;
69+
AuthConfigFile other = (AuthConfigFile) obj;
70+
if (authConfigMap == null) {
71+
if (other.authConfigMap != null)
72+
return false;
73+
} else if (!authConfigMap.equals(other.authConfigMap))
74+
return false;
75+
return true;
76+
}
77+
78+
@Override
79+
public String toString() {
80+
return "AuthConfigFile [authConfigMap=" + authConfigMap + "]";
81+
}
82+
83+
84+
public static AuthConfigFile loadConfig(File confFile) throws IOException {
85+
AuthConfigFile configFile = new AuthConfigFile();
86+
if (!confFile.exists()) {
87+
return new AuthConfigFile();
88+
}
89+
Map<String, AuthConfig> configMap = null;
90+
try {
91+
configMap = MAPPER.readValue(confFile, CONFIG_MAP_TYPE);
92+
} catch (IOException e) {
93+
// pass
94+
}
95+
if (configMap != null) {
96+
for (Map.Entry<String, AuthConfig> entry : configMap.entrySet()) {
97+
AuthConfig authConfig = entry.getValue();
98+
decodeAuth(authConfig.getAuth(), authConfig);
99+
authConfig.setAuth(null);
100+
authConfig.setServerAddress(entry.getKey());
101+
configFile.addConfig(authConfig);
102+
}
103+
} else {
104+
List<String> authFileContent = FileUtils.readLines(confFile);
105+
if (authFileContent.size() < 2) {
106+
throw new IOException("The Auth Config file is empty");
107+
}
108+
AuthConfig config = new AuthConfig();
109+
String[] origAuth = authFileContent.get(0).split(" = ");
110+
if (origAuth.length != 2) {
111+
throw new IOException("Invalid Auth config file");
112+
}
113+
decodeAuth(origAuth[1], config);
114+
115+
String[] origEmail = authFileContent.get(1).split(" = ");
116+
if (origEmail.length != 2) {
117+
throw new IOException("Invalid Auth config file");
118+
}
119+
config.setEmail(origEmail[1]);
120+
configFile.addConfig(config);
121+
}
122+
return configFile;
123+
124+
}
125+
126+
static void decodeAuth(String auth, AuthConfig config) throws IOException {
127+
String str = new String(Base64.decodeBase64(auth), Charset.forName("UTF-8"));
128+
String[] parts = str.split(":", 2);
129+
if (parts.length != 2) {
130+
throw new IOException("Invalid auth configuration file");
131+
}
132+
config.setUsername(parts[0]);
133+
config.setPassword(parts[1]);
134+
}
135+
136+
static String convertToHostname(String server) {
137+
String stripped = server;
138+
if (server.startsWith("http://")) {
139+
stripped = server.substring(7);
140+
} else if (server.startsWith("https://")) {
141+
stripped = server.substring(8);
142+
}
143+
String[] numParts = stripped.split("/", 2);
144+
return numParts[0];
145+
}
146+
}

src/main/java/com/github/dockerjava/core/DockerClientConfig.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class DockerClientConfig {
2323
// this is really confusing, as there are two ways to spell it
2424
private static final String DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY = "docker.io.enableLoggingFilter";
2525
private static final String DOCKER_IO_DOCKER_CERT_PATH_PROPERTY = "docker.io.dockerCertPath";
26+
private static final String DOCKER_IO_DOCKER_CFG_PATH_PROPERTY = "docker.io.dockerCfgPath";
2627
/**
2728
* A map from the environment name to the interval name.
2829
*/
@@ -36,21 +37,23 @@ public class DockerClientConfig {
3637
.put("DOCKER_READ_TIMEOUT", DOCKER_IO_READ_TIMEOUT_PROPERTY)
3738
.put("DOCKER_LOGGING_FILTER_ENABLED", DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY)
3839
.put(DOCKER_CERT_PATH_PROPERTY, DOCKER_IO_DOCKER_CERT_PATH_PROPERTY)
40+
.put("DOCKER_CFG_PATH", DOCKER_IO_DOCKER_CFG_PATH_PROPERTY)
3941
.build();
4042
private static final String DOCKER_IO_PROPERTIES_PROPERTY = "docker.io.properties";
4143
private final URI uri;
42-
private final String version, username, password, email, serverAddress, dockerCertPath;
44+
private final String version, username, password, email, serverAddress, dockerCertPath, dockerCfgPath;
4345
private final Integer readTimeout;
4446
private final boolean loggingFilterEnabled;
4547

46-
DockerClientConfig(URI uri, String version, String username, String password, String email, String serverAddress, String dockerCertPath, Integer readTimeout, boolean loggingFilterEnabled) {
48+
DockerClientConfig(URI uri, String version, String username, String password, String email, String serverAddress, String dockerCertPath, String dockerCfgPath, Integer readTimeout, boolean loggingFilterEnabled) {
4749
this.uri = uri;
4850
this.version = version;
4951
this.username = username;
5052
this.password = password;
5153
this.email = email;
5254
this.serverAddress = serverAddress;
5355
this.dockerCertPath = dockerCertPath;
56+
this.dockerCfgPath = dockerCfgPath;
5457
this.readTimeout = readTimeout;
5558
this.loggingFilterEnabled = loggingFilterEnabled;
5659
}
@@ -153,6 +156,7 @@ private static Properties overrideDockerPropertiesWithSystemProperties(Propertie
153156
DOCKER_IO_READ_TIMEOUT_PROPERTY,
154157
DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY,
155158
DOCKER_IO_DOCKER_CERT_PATH_PROPERTY,
159+
DOCKER_IO_DOCKER_CFG_PATH_PROPERTY,
156160
}) {
157161
if (systemProperties.containsKey(key)) {
158162
overriddenProperties.setProperty(key, systemProperties.getProperty(key));
@@ -212,6 +216,10 @@ public String getDockerCertPath() {
212216
return dockerCertPath;
213217
}
214218

219+
public String getDockerCfgPath() {
220+
return dockerCfgPath;
221+
}
222+
215223
@Override
216224
public boolean equals(Object o) {
217225
if (this == o) return true;
@@ -222,6 +230,8 @@ public boolean equals(Object o) {
222230
if (loggingFilterEnabled != that.loggingFilterEnabled) return false;
223231
if (dockerCertPath != null ? !dockerCertPath.equals(that.dockerCertPath) : that.dockerCertPath != null)
224232
return false;
233+
if (dockerCfgPath != null ? !dockerCfgPath.equals(that.dockerCfgPath) : that.dockerCfgPath != null)
234+
return false;
225235
if (email != null ? !email.equals(that.email) : that.email != null) return false;
226236
if (password != null ? !password.equals(that.password) : that.password != null) return false;
227237
if (readTimeout != null ? !readTimeout.equals(that.readTimeout) : that.readTimeout != null) return false;
@@ -243,6 +253,7 @@ public int hashCode() {
243253
result = 31 * result + (email != null ? email.hashCode() : 0);
244254
result = 31 * result + (serverAddress != null ? serverAddress.hashCode() : 0);
245255
result = 31 * result + (dockerCertPath != null ? dockerCertPath.hashCode() : 0);
256+
result = 31 * result + (dockerCfgPath != null ? dockerCfgPath.hashCode() : 0);
246257
result = 31 * result + (readTimeout != null ? readTimeout.hashCode() : 0);
247258
result = 31 * result + (loggingFilterEnabled ? 1 : 0);
248259
return result;
@@ -258,22 +269,23 @@ public String toString() {
258269
", email='" + email + '\'' +
259270
", serverAddress='" + serverAddress + '\'' +
260271
", dockerCertPath='" + dockerCertPath + '\'' +
272+
", dockerCfgPath='" + dockerCfgPath + '\'' +
261273
", readTimeout=" + readTimeout +
262274
", loggingFilterEnabled=" + loggingFilterEnabled +
263275
'}';
264276
}
265277

266278
public static class DockerClientConfigBuilder {
267279
private URI uri;
268-
private String version, username, password, email, serverAddress, dockerCertPath;
280+
private String version, username, password, email, serverAddress, dockerCertPath, dockerCfgPath;
269281
private Integer readTimeout;
270282
private boolean loggingFilterEnabled;
271283

272284
/**
273285
* This will set all fields in the builder to those contained in the Properties object. The Properties object
274-
* should contain the following docker.io.* keys: url, version, username, password, email, and dockerCertPath. If
275-
* docker.io.readTimeout or docker.io.enableLoggingFilter are not contained, they will be set to 1000 and true,
276-
* respectively.
286+
* should contain the following docker.io.* keys: url, version, username, password, email, dockerCertPath, and
287+
* dockerCfgPath. If docker.io.readTimeout or docker.io.enableLoggingFilter are not contained, they will be set
288+
* to 1000 and true, respectively.
277289
*/
278290
public DockerClientConfigBuilder withProperties(Properties p) {
279291
return withUri(p.getProperty(DOCKER_IO_URL_PROPERTY))
@@ -284,7 +296,8 @@ public DockerClientConfigBuilder withProperties(Properties p) {
284296
.withServerAddress(p.getProperty(DOCKER_IO_SERVER_ADDRESS_PROPERTY))
285297
.withReadTimeout(Integer.valueOf(p.getProperty(DOCKER_IO_READ_TIMEOUT_PROPERTY, "0")))
286298
.withLoggingFilter(Boolean.valueOf(p.getProperty(DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY, "true")))
287-
.withDockerCertPath(p.getProperty(DOCKER_IO_DOCKER_CERT_PATH_PROPERTY));
299+
.withDockerCertPath(p.getProperty(DOCKER_IO_DOCKER_CERT_PATH_PROPERTY))
300+
.withDockerCfgPath(p.getProperty(DOCKER_IO_DOCKER_CFG_PATH_PROPERTY));
288301
}
289302

290303
public final DockerClientConfigBuilder withUri(String uri) {
@@ -333,6 +346,12 @@ public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath)
333346
return this;
334347
}
335348

349+
public final DockerClientConfigBuilder withDockerCfgPath(String dockerCfgPath) {
350+
this.dockerCfgPath = dockerCfgPath;
351+
return this;
352+
}
353+
354+
336355
public DockerClientConfig build() {
337356
return new DockerClientConfig(
338357
uri,
@@ -342,6 +361,7 @@ public DockerClientConfig build() {
342361
email,
343362
serverAddress,
344363
dockerCertPath,
364+
dockerCfgPath,
345365
readTimeout,
346366
loggingFilterEnabled
347367
);

0 commit comments

Comments
 (0)