dev.galasa.docker.internal.DockerRegistryImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright contributors to the Galasa project
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.docker.internal;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Base64;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import dev.galasa.ICredentials;
import dev.galasa.ICredentialsToken;
import dev.galasa.ICredentialsUsername;
import dev.galasa.ICredentialsUsernamePassword;
import dev.galasa.ICredentialsUsernameToken;
import dev.galasa.docker.DockerManagerException;
import dev.galasa.docker.internal.properties.DockerImagePrefix;
import dev.galasa.docker.internal.properties.DockerRegistryCredentials;
import dev.galasa.docker.internal.properties.DockerRegistryURL;
import dev.galasa.framework.spi.ConfigurationPropertyStoreException;
import dev.galasa.framework.spi.IFramework;
import dev.galasa.framework.spi.creds.CredentialsException;
import dev.galasa.framework.spi.creds.ICredentialsService;
import dev.galasa.http.HttpClientException;
import dev.galasa.http.HttpClientResponse;
import dev.galasa.http.IHttpClient;
/**
* Docker RegistryImpl. Controls the location of where docker images can be
* pulled from
*
*
*/
public class DockerRegistryImpl {
private IFramework framework;
private DockerManagerImpl dockerManager;
private URL registryUrl;
private String registryId;
private String prefix;
private ICredentialsService credService;
private String registryRealmType;
private URL registryRealmURL;
private String authToken;
private IHttpClient client;
private IHttpClient realmClient;
private static final Log logger = LogFactory.getLog(DockerRegistryImpl.class);
/**
* Sets up the registry that the manager can use to pull images from.
*
* @param framework
* @param dockerManager
* @param registryId
* @throws DockerManagerException
*/
public DockerRegistryImpl(IFramework framework, DockerManagerImpl dockerManager, String registryId)
throws DockerManagerException {
this.framework = framework;
this.dockerManager = dockerManager;
this.registryId = registryId;
this.registryUrl = DockerRegistryURL.get(this);
this.prefix = DockerImagePrefix.get(this);
this.client = dockerManager.getHttpManager().newHttpClient();
this.realmClient = dockerManager.getHttpManager().newHttpClient();
try {
this.client.setURI(this.registryUrl.toURI());
this.credService = framework.getCredentialsService();
} catch (URISyntaxException e) {
logger.error("Registry URL is incompatible", e);
throw new DockerManagerException("Could not parse Docker registry URL.", e);
} catch (CredentialsException e) {
logger.error("Could not access credential store from framework.", e);
throw new DockerManagerException("Could not access credential store from framework.", e);
}
}
/**
* Checks the registry for and image.
*
* @param image
* @return true/false
*/
public boolean doYouHave(DockerImageImpl image) {
String resp = null;
String path = "";
try {
registryAuthenticate(image);
path = "/v2/" + getPrefix() + image.getImageName() + "/manifests/" + image.getTag();
logger.trace("Checking if image is available at location: " + path);
HttpClientResponse response = client.getJson(path);
if (response.getStatusCode() == (HttpStatus.SC_OK)) {
return true;
}
return false;
} catch (HttpClientException e) {
return false;
} catch (IllegalStateException e) {
return false;
} catch (DockerManagerException e) {
logger.trace("Failed to access registry", e);
return false;
} catch (ClassCastException e) {
logger.trace("Invalid JSON returned from Docker Registry\n" + resp, e);
return false;
}
}
/**
* Registry authentication
*
* @param image
* @throws DockerManagerException
*/
public void registryAuthenticate(DockerImageImpl image) throws DockerManagerException {
if (!retrieveRealm(image)) {
logger.info("No authentication required");
return;
}
if ("Bearer realm".equalsIgnoreCase(this.registryRealmType)) {
this.authToken = retrieveBearerToken();
return;
}
if ("Basic realm".equalsIgnoreCase(this.registryRealmType)) {
this.authToken = retrieveBasicToken();
return;
}
}
/**
* Attempts to gain a bearer token from realm, if unauthorized tries basic credentials login
* retreive token
*
* @return String token
* @throws DockerManagerException
*/
public String retrieveBearerToken() throws DockerManagerException {
try {
this.realmClient.setURI(this.registryRealmURL.toURI());
HttpClientResponse response = this.realmClient.getJson("");
if (response.getStatusCode() == (HttpStatus.SC_OK)) {
JsonObject json = response.getContent();
String token = json.get("token").getAsString();
this.client.addCommonHeader("Authorization", "Bearer "+token);
return token;
}
if (response.getStatusCode() == (HttpStatus.SC_UNAUTHORIZED)) {
Map headers = response.getheaders();
for (String key : headers.keySet()) {
if (key.equalsIgnoreCase("WWW-Authenticate")) {
String authType = parseAuthRealmType(headers.get(key));
if ("Basic realm".equals(authType)) {
return retrieveBasicToken();
} else {
throw new DockerManagerException("Dont know how to authenticate to registry: " + this.registryUrl);
}
}
}
}
throw new DockerManagerException("Failed to retrieve token from:" + this.registryRealmURL);
} catch (HttpClientException | URISyntaxException e) {
throw new DockerManagerException("Failed to connect to: " + this.registryRealmURL);
}
}
/**
* Uses basic crednetials to gain a basic auth token.
*
* @return String token
* @throws DockerManagerException
*/
public String retrieveBasicToken() throws DockerManagerException {
try {
ICredentials creds = getCreds();
if (creds instanceof ICredentialsUsernamePassword) {
String user = ((ICredentialsUsername) creds).getUsername();
String password = ((ICredentialsUsernamePassword) creds).getPassword();
this.client.setAuthorisation(user, password);
this.client.build();
return generateDockerRegistryAuthStructure(user, password);
}
if (creds instanceof ICredentialsUsernameToken) {
throw new DockerManagerException("Username tokens are not yet supported");
}
if (creds instanceof ICredentialsUsername) {
throw new DockerManagerException("Username credentials are not yet supported");
}
if (creds instanceof ICredentialsToken) {
throw new DockerManagerException("Tokens are not yet supported");
}
throw new DockerManagerException("Couldnt generate token");
} catch (DockerManagerException | CredentialsException e) {
throw new DockerManagerException("Couldnt locate credentials to generate token", e);
}
}
/**
* Retrieves credentials from the credential store with a given key
*
* @return ICredentials creds
* @throws ConfigurationPropertyStoreException
* @throws CredentialsException
*/
public ICredentials getCreds() throws DockerManagerException, CredentialsException {
String credKey = DockerRegistryCredentials.get(this);
return credService.getCredentials(credKey);
}
/**
* Returns boolean for if a auth realm can be found and identified.
*
* @param image
* @return
* @throws DockerManagerException
*/
boolean retrieveRealm(DockerImageImpl image) throws DockerManagerException {
String path = "/v2/" + getPrefix() + image.getImageName() + "/manifests/" + image.getTag();
try {
HttpClientResponse response = this.client.getJson(path);
if (response.getStatusCode() == (HttpStatus.SC_OK)) {
return false;
}
if (response.getStatusCode() == (HttpStatus.SC_UNAUTHORIZED)) {
Map headers = response.getheaders();
for (String key : headers.keySet()) {
if (key.equalsIgnoreCase("WWW-Authenticate")) {
this.registryRealmType = parseAuthRealmType(headers.get(key));
if (registryRealmType.equals("")) {
throw new DockerManagerException("Registry location '" + path + "' not found. Check to ensure image registry path is correct.");
}
if (!"BASIC realm".equalsIgnoreCase(this.registryRealmType)){
this.registryRealmURL = parseAuthRealmURL(headers.get(key));
}
return true;
}
}
}
throw new DockerManagerException("Failed to authenticate, and authentication is required.");
} catch (HttpClientException | MalformedURLException | JsonSyntaxException e) {
throw new DockerManagerException("Failed to connect to registry.", e);
}
}
/**
* Parses the realm type from the WWW-Authenticate header
*
* @param header
* @return String realmType
*/
private String parseAuthRealmType(String header) {
return header.split("=")[0];
}
/**
* Extracts the realm URL and builds the full URL to authenticate with the correct scope
*
* @param header
* @return URL authURL
* @throws MalformedURLException
*/
private URL parseAuthRealmURL(String header) throws MalformedURLException {
String manString = header.replaceAll("\"", "");
String[] components = (manString.substring(manString.indexOf("=")+1).split(","));
StringBuilder builder = new StringBuilder();
builder.append(components[0]);
builder.append("?");
for(int i=1;i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy