Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Last public release: [![Maven Central](https://maven-badges.herokuapp.com/maven-

## Changelog

### 1.12.2

* add Dependency: wiremock-jre8 (2.32.0)
* add code coverage to ``DefaultFileDownloader``
* fix bug on download files over proxy with Basic Auth
* add new configuration ``trustInsecureDownloadRoot`` to ignore insecure HTTPS downloads

### 1.12.1

* update Dependency: Jackson (2.13.0), Mockito (4.1.0), JUnit (5.8.1), Hamcrest (2.2; now a direct dependency)
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ present).

<!-- optional: where to download node and npm from. Defaults to https://nodejs.org/dist/ -->
<downloadRoot>http://myproxy.example.org/nodejs/</downloadRoot>

<!-- optional: ignore insecure HTTPS download connections -->
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
</configuration>
</plugin>
```
Expand All @@ -120,6 +123,8 @@ You can also specify separate download roots for npm and node as they are stored
<serverId>server001</serverId>
<!-- optional: where to download npm from. Defaults to https://registry.npmjs.org/npm/-/ -->
<npmDownloadRoot>https://myproxy.example.org/npm/</npmDownloadRoot>
<!-- optional: ignore insecure HTTPS download connections -->
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
</configuration>
</plugin>
```
Expand Down Expand Up @@ -166,7 +171,9 @@ https://github.com/eirslett/frontend-maven-plugin/blob/master/frontend-maven-plu
<!-- optional: where to download node from. Defaults to https://nodejs.org/dist/ -->
<nodeDownloadRoot>http://myproxy.example.org/nodejs/</nodeDownloadRoot>
<!-- optional: where to download yarn from. Defaults to https://github.com/yarnpkg/yarn/releases/download/ -->
<yarnDownloadRoot>http://myproxy.example.org/yarn/</yarnDownloadRoot>
<yarnDownloadRoot>http://myproxy.example.org/yarn/</yarnDownloadRoot>
<!-- optional: ignore insecure HTTPS download connections -->
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
</configuration>
</plugin>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public final class InstallNodeAndNpmMojo extends AbstractFrontendMojo {
@Deprecated
private String downloadRoot;

/**
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #npmDownloadRoot}.
*/
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
private boolean trustInsecureDownloadRoot;

/**
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example 'v0.10.18'
*/
Expand Down Expand Up @@ -81,6 +87,7 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
factory.getNodeInstaller(proxyConfig)
.setNodeVersion(nodeVersion)
.setNodeDownloadRoot(nodeDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setNpmVersion(npmVersion)
.setUserName(server.getUsername())
.setPassword(server.getPassword())
Expand All @@ -89,19 +96,22 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
.setNodeVersion(nodeVersion)
.setNpmVersion(npmVersion)
.setNpmDownloadRoot(npmDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setUserName(server.getUsername())
.setPassword(server.getPassword())
.install();
} else {
factory.getNodeInstaller(proxyConfig)
.setNodeVersion(nodeVersion)
.setNodeDownloadRoot(nodeDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setNpmVersion(npmVersion)
.install();
factory.getNPMInstaller(proxyConfig)
.setNodeVersion(this.nodeVersion)
.setNpmVersion(this.npmVersion)
.setNpmDownloadRoot(npmDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.install();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public final class InstallNodeAndPnpmMojo extends AbstractFrontendMojo {
@Deprecated
private String downloadRoot;

/**
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #pnpmDownloadRoot}.
*/
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
private boolean trustInsecureDownloadRoot;

/**
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example 'v0.10.18'
*/
Expand Down Expand Up @@ -81,25 +87,29 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
factory.getNodeInstaller(proxyConfig)
.setNodeVersion(nodeVersion)
.setNodeDownloadRoot(nodeDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setNpmVersion(pnpmVersion)
.setUserName(server.getUsername())
.setPassword(server.getPassword())
.install();
factory.getPNPMInstaller(proxyConfig)
.setPnpmVersion(pnpmVersion)
.setPnpmDownloadRoot(npmDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setUserName(server.getUsername())
.setPassword(server.getPassword())
.install();
} else {
factory.getNodeInstaller(proxyConfig)
.setNodeVersion(nodeVersion)
.setNodeDownloadRoot(nodeDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setNpmVersion(pnpmVersion)
.install();
factory.getPNPMInstaller(proxyConfig)
.setPnpmVersion(this.pnpmVersion)
.setPnpmDownloadRoot(npmDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.install();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public final class InstallNodeAndYarnMojo extends AbstractFrontendMojo {
defaultValue = YarnInstaller.DEFAULT_YARN_DOWNLOAD_ROOT)
private String yarnDownloadRoot;

/**
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #yarnDownloadRoot}.
*/
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
private boolean trustInsecureDownloadRoot;

/**
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example
* 'v0.10.18'
Expand Down Expand Up @@ -91,9 +97,11 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
Server server = MojoUtils.decryptServer(this.serverId, this.session, this.decrypter);
if (null != server) {
factory.getNodeInstaller(proxyConfig).setNodeDownloadRoot(this.nodeDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setNodeVersion(this.nodeVersion).setPassword(server.getPassword())
.setUserName(server.getUsername()).install();
factory.getYarnInstaller(proxyConfig).setYarnDownloadRoot(this.yarnDownloadRoot)
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
.setYarnVersion(this.yarnVersion).setUserName(server.getUsername())
.setPassword(server.getPassword()).setIsYarnBerry(isYarnrcYamlFilePresent()).install();
} else {
Expand Down
6 changes: 6 additions & 0 deletions frontend-plugin-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.32.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,31 @@
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.codehaus.plexus.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -41,7 +49,7 @@ public DownloadException(String message){
}

interface FileDownloader {
void download(String downloadUrl, String destination, String userName, String password) throws DownloadException;
void download(String downloadUrl, String destination, String userName, String password, boolean trustInsecureDownloadRoot) throws DownloadException;
}

final class DefaultFileDownloader implements FileDownloader {
Expand All @@ -54,7 +62,7 @@ public DefaultFileDownloader(ProxyConfig proxyConfig){
}

@Override
public void download(String downloadUrl, String destination, String userName, String password) throws DownloadException {
public void download(String downloadUrl, String destination, String userName, String password, boolean trustInsecureDownloadRoot) throws DownloadException {
// force tls to 1.2 since github removed weak cryptographic standards
// https://blog.github.com/2018-02-02-weak-cryptographic-standards-removal-notice/
System.setProperty("https.protocols", "TLSv1.2");
Expand All @@ -66,7 +74,7 @@ public void download(String downloadUrl, String destination, String userName, St
FileUtils.copyFile(new File(downloadURI), new File(destination));
}
else {
CloseableHttpResponse response = execute(fixedDownloadUrl, userName, password);
CloseableHttpResponse response = execute(fixedDownloadUrl, userName, password, trustInsecureDownloadRoot);
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode != 200){
throw new DownloadException("Got error code "+ statusCode +" from the server.");
Expand All @@ -82,12 +90,18 @@ public void download(String downloadUrl, String destination, String userName, St
}
}

private CloseableHttpResponse execute(String requestUrl, String userName, String password) throws IOException {
private CloseableHttpResponse execute(String requestUrl, String userName, String password, boolean trustInsecureDownloadRoot)
throws IOException, DownloadException {
CloseableHttpResponse response;
SSLContext sslContext = null;
if (trustInsecureDownloadRoot) {
LOGGER.info("Trust insecure download enabled.");
sslContext = makeSSLContextForTrustedInsecureDownloads();
}
Proxy proxy = proxyConfig.getProxyForUrl(requestUrl);
if (proxy != null) {
LOGGER.info("Downloading via proxy " + proxy.toString());
return executeViaProxy(proxy, requestUrl);
return executeViaProxy(proxy, sslContext, requestUrl);
} else {
LOGGER.info("No proxy was configured, downloading directly");
if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password)) {
Expand All @@ -100,40 +114,50 @@ private CloseableHttpResponse execute(String requestUrl, String userName, String
aURL.getPort(),
userName,
password);
response = buildHttpClient(credentialsProvider).execute(new HttpGet(requestUrl),localContext);
response = buildHttpClient(sslContext, credentialsProvider).execute(new HttpGet(requestUrl),localContext);
} else {
response = buildHttpClient(null).execute(new HttpGet(requestUrl));
response = buildHttpClient(sslContext, null).execute(new HttpGet(requestUrl));
}
}
return response;
}

private CloseableHttpResponse executeViaProxy(Proxy proxy, String requestUrl) throws IOException {
final CloseableHttpClient proxyClient;
if (proxy.useAuthentication()){
proxyClient = buildHttpClient(makeCredentialsProvider(proxy.host,proxy.port,proxy.username,proxy.password));
private CloseableHttpResponse executeViaProxy(Proxy proxy, SSLContext sslContext, String requestUrl) throws IOException {
final HttpGet request = new HttpGet(requestUrl);
request.setConfig(RequestConfig.custom()
.setProxy(new HttpHost(proxy.host, proxy.port))
.build());
final CloseableHttpClient proxyClient = buildHttpClient(sslContext, null);
if (proxy.useAuthentication()) {
final AuthState authState = new AuthState();
authState.update(new BasicScheme(), new UsernamePasswordCredentials(proxy.username, proxy.password));
final HttpClientContext httpContext = HttpClientContext.create();
httpContext.setAttribute(HttpClientContext.PROXY_AUTH_STATE, authState);
return proxyClient.execute(request, httpContext);
} else {
proxyClient = buildHttpClient(null);
return proxyClient.execute(request);
}

final HttpHost proxyHttpHost = new HttpHost(proxy.host, proxy.port);

final RequestConfig requestConfig = RequestConfig.custom().setProxy(proxyHttpHost).build();

final HttpGet request = new HttpGet(requestUrl);
request.setConfig(requestConfig);

return proxyClient.execute(request);
}

private CloseableHttpClient buildHttpClient(CredentialsProvider credentialsProvider) {
private CloseableHttpClient buildHttpClient(SSLContext sslContext, CredentialsProvider credentialsProvider) {
return HttpClients.custom()
.setSSLContext(sslContext)
.disableContentCompression()
.useSystemProperties()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
}

private SSLContext makeSSLContextForTrustedInsecureDownloads() throws DownloadException {
try {
return SSLContextBuilder.create()
.loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE)
.build();
} catch (final NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
throw new DownloadException("Unable to create SSLContext to trust insecure downloads.", e);
}
}

private CredentialsProvider makeCredentialsProvider(String host, int port, String username, String password) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class NPMInstaller {

private String nodeVersion, npmVersion, npmDownloadRoot, userName, password;

private boolean trustInsecureDownloadRoot;

private final Logger logger;

private final InstallConfig config;
Expand Down Expand Up @@ -50,6 +52,11 @@ public NPMInstaller setNpmDownloadRoot(String npmDownloadRoot) {
return this;
}

public NPMInstaller setTrustInsecureDownloadRoot(boolean trustInsecureDownloadRoot) {
this.trustInsecureDownloadRoot = trustInsecureDownloadRoot;
return this;
}

public NPMInstaller setUserName(String userName) {
this.userName = userName;
return this;
Expand Down Expand Up @@ -122,7 +129,7 @@ private void installNpm() throws InstallationException {

File archive = this.config.getCacheResolver().resolve(cacheDescriptor);

downloadFileIfMissing(downloadUrl, archive, this.userName, this.password);
downloadFileIfMissing(downloadUrl, archive, this.userName, this.password, this.trustInsecureDownloadRoot);

File installDirectory = getNodeInstallDirectory();
File nodeModulesDirectory = new File(installDirectory, "node_modules");
Expand Down Expand Up @@ -218,16 +225,16 @@ private void extractFile(File archive, File destinationDirectory) throws Archive
this.archiveExtractor.extract(archive.getPath(), destinationDirectory.getPath());
}

private void downloadFileIfMissing(String downloadUrl, File destination, String userName, String password)
private void downloadFileIfMissing(String downloadUrl, File destination, String userName, String password, boolean trustInsecureDownloadRoot)
throws DownloadException {
if (!destination.exists()) {
downloadFile(downloadUrl, destination, userName, password);
downloadFile(downloadUrl, destination, userName, password, trustInsecureDownloadRoot);
}
}

private void downloadFile(String downloadUrl, File destination, String userName, String password)
private void downloadFile(String downloadUrl, File destination, String userName, String password, boolean trustInsecureDownloadRoot)
throws DownloadException {
this.logger.info("Downloading {} to {}", downloadUrl, destination);
this.fileDownloader.download(downloadUrl, destination.getPath(), userName, password);
this.fileDownloader.download(downloadUrl, destination.getPath(), userName, password, trustInsecureDownloadRoot);
}
}
Loading