diff --git a/spring-boot-cli/pom.xml b/spring-boot-cli/pom.xml index b6ce765cb9e..0aa1b0bac80 100644 --- a/spring-boot-cli/pom.xml +++ b/spring-boot-cli/pom.xml @@ -67,6 +67,16 @@ org.apache.maven maven-settings-builder + + org.codehaus.plexus + plexus-component-api + + + * + * + + + org.eclipse.aether aether-api diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java index 1f79b5cf43c..62a25b30ffb 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java @@ -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 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 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); + } + } + } } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java index 19c48334e1e..123f81bb486 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java @@ -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)); } } diff --git a/spring-boot-cli/src/test/resources/maven-settings/basic/.m2/settings.xml b/spring-boot-cli/src/test/resources/maven-settings/basic/.m2/settings.xml new file mode 100644 index 00000000000..b2b97db3302 --- /dev/null +++ b/spring-boot-cli/src/test/resources/maven-settings/basic/.m2/settings.xml @@ -0,0 +1,31 @@ + + + + + my-mirror + http://maven.example.com/mirror + my-server + + + + + + my-server + tester + secret + + + + + + my-proxy + true + http + proxy.example.com + 8080 + proxyuser + somepassword + + + + \ No newline at end of file diff --git a/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml b/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml new file mode 100644 index 00000000000..7b6597c44e9 --- /dev/null +++ b/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml @@ -0,0 +1,3 @@ + + {oAyWuFO63U8HHgiplpqtgXih0/pwcRA0d+uA+Z7TBEk=} + \ No newline at end of file diff --git a/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings.xml b/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings.xml new file mode 100644 index 00000000000..b8701c783a1 --- /dev/null +++ b/spring-boot-cli/src/test/resources/maven-settings/encrypted/.m2/settings.xml @@ -0,0 +1,31 @@ + + + + + my-mirror + http://maven.example.com/mirror + my-server + + + + + + my-server + tester + {Ur5BpeQGlYUHhXsHahO/HbMBcPSFSUtN5gbWuFFPYGw=} + + + + + + my-proxy + true + http + proxy.example.com + 8080 + proxyuser + {3iRQQyaIUgQHwH8uzTvr9/52pZAjLOTWz/SlWDB7CM4=} + + + + \ No newline at end of file diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml index 2c581b6bdf7..afbb5bf3cc7 100644 --- a/spring-boot-parent/pom.xml +++ b/spring-boot-parent/pom.xml @@ -98,6 +98,11 @@ plexus-archiver 2.4.4 + + org.codehaus.plexus + plexus-component-api + 1.0-alpha-33 + org.codehaus.plexus plexus-utils