Enable support for use of encryption in Maven's settings.xml
This commit updates the CLI so that it will decrypt any encrypted passwords in a user's Maven settings.xml file. The code that performs the decrytion has a transitive dependency on three types in Plexus' logging API. There are tens of different artifacts containing this API available in Maven Central. Rather than bloating the API with a dependency on a complete Plexus container, which could perhaps be considered the primary source, a dependency on a considerably smaller artifact has been introduced. Closes #574
This commit is contained in:
parent
c5820d872a
commit
b8858bdb8f
|
@ -67,6 +67,16 @@
|
|||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-settings-builder</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-component-api</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.aether</groupId>
|
||||
<artifactId>aether-api</artifactId>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.springframework.boot.cli.compiler.grape;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.settings.Mirror;
|
||||
import org.apache.maven.settings.Proxy;
|
||||
|
@ -26,6 +28,10 @@ import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
|
|||
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
|
||||
import org.apache.maven.settings.building.SettingsBuildingException;
|
||||
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
||||
import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
|
||||
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
|
||||
import org.apache.maven.settings.crypto.SettingsDecrypter;
|
||||
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.repository.Authentication;
|
||||
|
@ -38,6 +44,9 @@ import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
|
|||
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
|
||||
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
|
||||
import org.eclipse.aether.util.repository.DefaultProxySelector;
|
||||
import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
|
||||
import org.sonatype.plexus.components.cipher.PlexusCipherException;
|
||||
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
|
||||
|
||||
/**
|
||||
* Auto-configuration for a RepositorySystemSession that uses Maven's settings.xml to
|
||||
|
@ -48,18 +57,34 @@ import org.eclipse.aether.util.repository.DefaultProxySelector;
|
|||
public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
||||
RepositorySystemSessionAutoConfiguration {
|
||||
|
||||
private static final String HOME_DIR = System.getProperty("user.home");
|
||||
private static final String DEFAULT_HOME_DIR = System.getProperty("user.home");
|
||||
|
||||
private final String homeDir;
|
||||
|
||||
public SettingsXmlRepositorySystemSessionAutoConfiguration() {
|
||||
this(DEFAULT_HOME_DIR);
|
||||
}
|
||||
|
||||
SettingsXmlRepositorySystemSessionAutoConfiguration(String homeDir) {
|
||||
this.homeDir = homeDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(DefaultRepositorySystemSession session,
|
||||
RepositorySystem repositorySystem) {
|
||||
|
||||
Settings settings = loadSettings();
|
||||
SettingsDecryptionResult decryptionResult = decryptSettings(settings);
|
||||
if (!decryptionResult.getProblems().isEmpty()) {
|
||||
throw new IllegalStateException("Settings decryption failed: "
|
||||
+ decryptionResult.getProblems());
|
||||
}
|
||||
|
||||
session.setOffline(settings.isOffline());
|
||||
session.setMirrorSelector(createMirrorSelector(settings));
|
||||
session.setAuthenticationSelector(createAuthenticationSelector(settings));
|
||||
session.setProxySelector(createProxySelector(settings));
|
||||
session.setAuthenticationSelector(createAuthenticationSelector(decryptionResult
|
||||
.getServers()));
|
||||
session.setProxySelector(createProxySelector(decryptionResult.getProxies()));
|
||||
|
||||
String localRepository = settings.getLocalRepository();
|
||||
if (localRepository != null) {
|
||||
|
@ -69,7 +94,7 @@ public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
|||
}
|
||||
|
||||
private Settings loadSettings() {
|
||||
File settingsFile = new File(HOME_DIR, ".m2/settings.xml");
|
||||
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
|
||||
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
|
||||
request.setUserSettingsFile(settingsFile);
|
||||
try {
|
||||
|
@ -82,6 +107,32 @@ public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
|||
}
|
||||
}
|
||||
|
||||
private SettingsDecryptionResult decryptSettings(Settings settings) {
|
||||
DefaultSettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(
|
||||
settings);
|
||||
|
||||
return createSettingsDecrypter().decrypt(request);
|
||||
}
|
||||
|
||||
private SettingsDecrypter createSettingsDecrypter() {
|
||||
SettingsDecrypter settingsDecrypter = new DefaultSettingsDecrypter();
|
||||
setField(DefaultSettingsDecrypter.class, "securityDispatcher", settingsDecrypter,
|
||||
new SpringBootSecDispatcher());
|
||||
return settingsDecrypter;
|
||||
}
|
||||
|
||||
private void setField(Class<?> clazz, String fieldName, Object target, Object value) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to set field '" + fieldName
|
||||
+ "' on '" + target + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
private MirrorSelector createMirrorSelector(Settings settings) {
|
||||
DefaultMirrorSelector selector = new DefaultMirrorSelector();
|
||||
for (Mirror mirror : settings.getMirrors()) {
|
||||
|
@ -91,9 +142,9 @@ public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
|||
return selector;
|
||||
}
|
||||
|
||||
private AuthenticationSelector createAuthenticationSelector(Settings settings) {
|
||||
private AuthenticationSelector createAuthenticationSelector(List<Server> servers) {
|
||||
DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
|
||||
for (Server server : settings.getServers()) {
|
||||
for (Server server : servers) {
|
||||
AuthenticationBuilder auth = new AuthenticationBuilder();
|
||||
auth.addUsername(server.getUsername()).addPassword(server.getPassword());
|
||||
auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
|
||||
|
@ -102,9 +153,9 @@ public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
|||
return new ConservativeAuthenticationSelector(selector);
|
||||
}
|
||||
|
||||
private ProxySelector createProxySelector(Settings settings) {
|
||||
private ProxySelector createProxySelector(List<Proxy> proxies) {
|
||||
DefaultProxySelector selector = new DefaultProxySelector();
|
||||
for (Proxy proxy : settings.getProxies()) {
|
||||
for (Proxy proxy : proxies) {
|
||||
Authentication authentication = new AuthenticationBuilder()
|
||||
.addUsername(proxy.getUsername()).addPassword(proxy.getPassword())
|
||||
.build();
|
||||
|
@ -114,4 +165,19 @@ public class SettingsXmlRepositorySystemSessionAutoConfiguration implements
|
|||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
private class SpringBootSecDispatcher extends DefaultSecDispatcher {
|
||||
|
||||
public SpringBootSecDispatcher() {
|
||||
this._configurationFile = new File(
|
||||
SettingsXmlRepositorySystemSessionAutoConfiguration.this.homeDir,
|
||||
".m2/settings-security.xml").getAbsolutePath();
|
||||
try {
|
||||
this._cipher = new DefaultPlexusCipher();
|
||||
}
|
||||
catch (PlexusCipherException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,11 @@ import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
|
|||
import org.apache.maven.settings.building.SettingsBuildingException;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.repository.Authentication;
|
||||
import org.eclipse.aether.repository.AuthenticationContext;
|
||||
import org.eclipse.aether.repository.LocalRepositoryManager;
|
||||
import org.eclipse.aether.repository.Proxy;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
@ -28,6 +32,7 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
|
@ -49,12 +54,60 @@ public class SettingsXmlRepositorySystemSessionAutoConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void basicSessionCustomization() throws SettingsBuildingException {
|
||||
assertSessionCustomization("src/test/resources/maven-settings/basic");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptedSettingsSessionCustomization() throws SettingsBuildingException {
|
||||
assertSessionCustomization("src/test/resources/maven-settings/encrypted");
|
||||
}
|
||||
|
||||
private void assertSessionCustomization(String userHome) {
|
||||
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
||||
|
||||
new SettingsXmlRepositorySystemSessionAutoConfiguration().apply(session,
|
||||
new SettingsXmlRepositorySystemSessionAutoConfiguration(userHome).apply(session,
|
||||
this.repositorySystem);
|
||||
|
||||
assertNotNull(session.getMirrorSelector());
|
||||
assertNotNull(session.getProxySelector());
|
||||
RemoteRepository repository = new RemoteRepository.Builder("my-server",
|
||||
"default", "http://maven.example.com").build();
|
||||
|
||||
assertMirrorSelectorConfiguration(session, repository);
|
||||
assertProxySelectorConfiguration(session, repository);
|
||||
assertAuthenticationSelectorConfiguration(session, repository);
|
||||
}
|
||||
|
||||
private void assertProxySelectorConfiguration(DefaultRepositorySystemSession session,
|
||||
RemoteRepository repository) {
|
||||
Proxy proxy = session.getProxySelector().getProxy(repository);
|
||||
repository = new RemoteRepository.Builder(repository).setProxy(proxy).build();
|
||||
AuthenticationContext authenticationContext = AuthenticationContext.forProxy(
|
||||
session, repository);
|
||||
assertEquals("proxy.example.com", proxy.getHost());
|
||||
assertEquals("proxyuser",
|
||||
authenticationContext.get(AuthenticationContext.USERNAME));
|
||||
assertEquals("somepassword",
|
||||
authenticationContext.get(AuthenticationContext.PASSWORD));
|
||||
}
|
||||
|
||||
private void assertMirrorSelectorConfiguration(
|
||||
DefaultRepositorySystemSession session, RemoteRepository repository) {
|
||||
RemoteRepository mirror = session.getMirrorSelector().getMirror(repository);
|
||||
assertNotNull("No mirror configured for repository " + repository.getId(), mirror);
|
||||
assertEquals("maven.example.com", mirror.getHost());
|
||||
}
|
||||
|
||||
private void assertAuthenticationSelectorConfiguration(
|
||||
DefaultRepositorySystemSession session, RemoteRepository repository) {
|
||||
Authentication authentication = session.getAuthenticationSelector()
|
||||
.getAuthentication(repository);
|
||||
|
||||
repository = new RemoteRepository.Builder(repository).setAuthentication(
|
||||
authentication).build();
|
||||
|
||||
AuthenticationContext authenticationContext = AuthenticationContext
|
||||
.forRepository(session, repository);
|
||||
|
||||
assertEquals("tester", authenticationContext.get(AuthenticationContext.USERNAME));
|
||||
assertEquals("secret", authenticationContext.get(AuthenticationContext.PASSWORD));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<settings>
|
||||
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>my-mirror</id>
|
||||
<url>http://maven.example.com/mirror</url>
|
||||
<mirrorOf>my-server</mirrorOf>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<id>my-server</id>
|
||||
<username>tester</username>
|
||||
<password>secret</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<proxies>
|
||||
<proxy>
|
||||
<id>my-proxy</id>
|
||||
<active>true</active>
|
||||
<protocol>http</protocol>
|
||||
<host>proxy.example.com</host>
|
||||
<port>8080</port>
|
||||
<username>proxyuser</username>
|
||||
<password>somepassword</password>
|
||||
</proxy>
|
||||
</proxies>
|
||||
|
||||
</settings>
|
|
@ -0,0 +1,3 @@
|
|||
<settingsSecurity>
|
||||
<master>{oAyWuFO63U8HHgiplpqtgXih0/pwcRA0d+uA+Z7TBEk=}</master>
|
||||
</settingsSecurity>
|
|
@ -0,0 +1,31 @@
|
|||
<settings>
|
||||
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>my-mirror</id>
|
||||
<url>http://maven.example.com/mirror</url>
|
||||
<mirrorOf>my-server</mirrorOf>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<id>my-server</id>
|
||||
<username>tester</username>
|
||||
<password>{Ur5BpeQGlYUHhXsHahO/HbMBcPSFSUtN5gbWuFFPYGw=}</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<proxies>
|
||||
<proxy>
|
||||
<id>my-proxy</id>
|
||||
<active>true</active>
|
||||
<protocol>http</protocol>
|
||||
<host>proxy.example.com</host>
|
||||
<port>8080</port>
|
||||
<username>proxyuser</username>
|
||||
<password>{3iRQQyaIUgQHwH8uzTvr9/52pZAjLOTWz/SlWDB7CM4=}</password>
|
||||
</proxy>
|
||||
</proxies>
|
||||
|
||||
</settings>
|
|
@ -98,6 +98,11 @@
|
|||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>2.4.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-component-api</artifactId>
|
||||
<version>1.0-alpha-33</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
|
|
Loading…
Reference in New Issue