package com.github.dockerjava.core;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.AuthConfigurations;
public class AuthConfigFile {
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final TypeReference<Map<String, AuthConfig>> CONFIG_CFG_MAP_TYPE =
new TypeReference<Map<String, AuthConfig>>() {
};
private static final TypeReference<Map<String, Map<String, AuthConfig>>> CONFIG_JSON_MAP_TYPE =
new TypeReference<Map<String, Map<String, AuthConfig>>>() {
};
private final Map<String, AuthConfig> authConfigMap;
public AuthConfigFile() {
authConfigMap = new HashMap<String, AuthConfig>();
}
void addConfig(AuthConfig config) {
authConfigMap.put(config.getRegistryAddress(), config);
}
public AuthConfig resolveAuthConfig(String hostname) {
if (StringUtils.isEmpty(hostname) || AuthConfig.DEFAULT_SERVER_ADDRESS.equals(hostname)) {
return authConfigMap.get(AuthConfig.DEFAULT_SERVER_ADDRESS);
}
AuthConfig c = authConfigMap.get(hostname);
if (c != null) {
return c;
}
// Maybe they have a legacy config file, we will iterate the keys converting
// them to the new format and testing
String normalizedHostname = convertToHostname(hostname);
for (Map.Entry<String, AuthConfig> entry : authConfigMap.entrySet()) {
String registry = entry.getKey();
AuthConfig config = entry.getValue();
if (convertToHostname(registry).equals(normalizedHostname)) {
return config;
}
}
return null;
}
public AuthConfigurations getAuthConfigurations() {
final AuthConfigurations authConfigurations = new AuthConfigurations();
for (Map.Entry<String, AuthConfig> authConfigEntry : authConfigMap.entrySet()) {
authConfigurations.addConfig(authConfigEntry.getValue());
}
return authConfigurations;
}
// CHECKSTYLE:OFF
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((authConfigMap == null) ? 0 : authConfigMap.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AuthConfigFile other = (AuthConfigFile) obj;
if (authConfigMap == null) {
if (other.authConfigMap != null)
return false;
} else if (!authConfigMap.equals(other.authConfigMap))
return false;
return true;
}
// CHECKSTYLE:ON
@Override
public String toString() {
return "AuthConfigFile [authConfigMap=" + authConfigMap + "]";
}
public static AuthConfigFile loadConfig(File confFile) throws IOException {
AuthConfigFile configFile = new AuthConfigFile();
if (!confFile.exists()) {
return new AuthConfigFile();
}
Map<String, AuthConfig> configMap = null;
/*
Registry v2 expects config expects config.json while v2 expects .dockercfg
The only difference between them is that config.json wraps "auths" around the AuthConfig
*/
try {
// try registry version 2
Map<String, Map<String, AuthConfig>> configJson = MAPPER.readValue(confFile, CONFIG_JSON_MAP_TYPE);
if (configJson != null) {
configMap = configJson.get("auths");
}
} catch (IOException e1) {
try {
// try registry version 1
configMap = MAPPER.readValue(confFile, CONFIG_CFG_MAP_TYPE);
} catch (IOException e2) {
// pass
}
}
if (configMap != null) {
for (Map.Entry<String, AuthConfig> entry : configMap.entrySet()) {
AuthConfig authConfig = entry.getValue();
decodeAuth(authConfig.getAuth(), authConfig);
authConfig.withAuth(null);
authConfig.withRegistryAddress(entry.getKey());
configFile.addConfig(authConfig);
}
} else {
List<String> authFileContent = FileUtils.readLines(confFile);
if (authFileContent.size() < 2) {
throw new IOException("The Auth Config file is empty");
}
AuthConfig config = new AuthConfig();
String[] origAuth = authFileContent.get(0).split(" = ");
if (origAuth.length != 2) {
throw new IOException("Invalid Auth config file");
}
decodeAuth(origAuth[1], config);
String[] origEmail = authFileContent.get(1).split(" = ");
if (origEmail.length != 2) {
throw new IOException("Invalid Auth config file");
}
config.withEmail(origEmail[1]);
configFile.addConfig(config);
}
return configFile;
}
static void decodeAuth(String auth, AuthConfig config) throws IOException {
String str = new String(Base64.decodeBase64(auth), Charset.forName("UTF-8"));
String[] parts = str.split(":", 2);
if (parts.length != 2) {
throw new IOException("Invalid auth configuration file");
}
config.withUsername(parts[0]);
config.withPassword(parts[1]);
}
static String convertToHostname(String server) {
String stripped = server;
if (server.startsWith("http://")) {
stripped = server.substring(7);
} else if (server.startsWith("https://")) {
stripped = server.substring(8);
}
String[] numParts = stripped.split("/", 2);
return numParts[0];
}
}