MNG-5795: Verify that core extensions can be retrieved when a mirror is used that requires basic auth.

This commit is contained in:
Jason van Zyl 2015-04-02 00:18:55 -04:00
parent 2fee3bbc73
commit 030eb3cf6f
5 changed files with 387 additions and 90 deletions

View File

@ -1,25 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
Licensed to the Apache Software Foundation (ASF) under one license agreements. See the NOTICE file distributed with this work for additional
or more contributor license agreements. See the NOTICE file information regarding copyright ownership. The ASF licenses this file to
distributed with this work for additional information you under the Apache License, Version 2.0 (the "License"); you may not use
regarding copyright ownership. The ASF licenses this file this file except in compliance with the License. You may obtain a copy of
to you under the Apache License, Version 2.0 (the the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
"License"); you may not use this file except in compliance by applicable law or agreed to in writing, software distributed under the
with the License. You may obtain a copy of the License at License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License. -->
http://www.apache.org/licenses/LICENSE-2.0 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
@ -33,40 +26,31 @@ under the License.
<name>Maven ITs</name> <name>Maven ITs</name>
<description>The effective Maven Integration Tests suite.</description> <description>The effective Maven Integration Tests suite.</description>
<!-- <!-- By default, the project just packages the tests in an artifact. To
By default, the project just packages the tests in an artifact. To actually run them, activate the profile "run-its": actually run them, activate the profile "run-its": mvn clean test -Prun-its
This will subject the Maven version running the build to the integration
mvn clean test -Prun-its tests. If you would like to test a different Maven distribution, you can
use the system property "mavenHome" to specify the path of the Maven distribution
This will subject the Maven version running the build to the integration tests. If you would like to test a different to test: mvn clean test -Prun-its -DmavenHome=<maven-under-test> Alternatively,
Maven distribution, you can use the system property "mavenHome" to specify the path of the Maven distribution to test: you can just specify the version of a previously installed/deployed Maven
distribution which will be downloaded, unpacked and tested: mvn clean test
mvn clean test -Prun-its -DmavenHome=<maven-under-test> -Prun-its -DmavenVersion=2.2.1 It's also possible to point the ITs at an
already downloaded Maven distribution: mvn clean test -Prun-its -DmavenDistro=<path-to-bin-archive>
Alternatively, you can just specify the version of a previously installed/deployed Maven distribution which will be To run the ITs using embedded Maven 3.x, additionally activate the profile
downloaded, unpacked and tested: "embedded". ITs that don't require to fork Maven can also be run from the
IDE using the Maven projects from the workspace if the Maven dependencies
mvn clean test -Prun-its -DmavenVersion=2.2.1 are added to the test class path. If you're behind a proxy, use the system
properties proxy.host, proxy.port, proxy.user and proxy.pass to specify the
It's also possible to point the ITs at an already downloaded Maven distribution: required proxy setup for the ITs. Alternatively, set the system property
maven.it.central to a URL of a local repository manager that proxies the
mvn clean test -Prun-its -DmavenDistro=<path-to-bin-archive> required artifacts. -->
To run the ITs using embedded Maven 3.x, additionally activate the profile "embedded".
ITs that don't require to fork Maven can also be run from the IDE using the Maven projects from the workspace if the
Maven dependencies are added to the test class path.
If you're behind a proxy, use the system properties proxy.host, proxy.port, proxy.user and proxy.pass to specify the
required proxy setup for the ITs. Alternatively, set the system property maven.it.central to a URL of a local
repository manager that proxies the required artifacts.
-->
<properties> <properties>
<surefireMemory>-Xmx384m -XX:MaxPermSize=192m</surefireMemory> <surefireMemory>-Xmx384m -XX:MaxPermSize=192m</surefireMemory>
<!-- The original Maven distribution to test. --> <!-- The original Maven distribution to test. -->
<mavenHome>${maven.home}</mavenHome> <mavenHome>${maven.home}</mavenHome>
<!-- The (possibly instrumented copy of the) Maven distribution we actually use for the tests. --> <!-- The (possibly instrumented copy of the) Maven distribution we actually
use for the tests. -->
<preparedMavenHome>${mavenHome}</preparedMavenHome> <preparedMavenHome>${mavenHome}</preparedMavenHome>
<!-- default properties used to filter the global settings --> <!-- default properties used to filter the global settings -->
<maven.it.central>http://repo1.maven.org/maven2</maven.it.central> <maven.it.central>http://repo1.maven.org/maven2</maven.it.central>
@ -77,6 +61,7 @@ under the License.
<proxy.user></proxy.user> <proxy.user></proxy.user>
<proxy.pass></proxy.pass> <proxy.pass></proxy.pass>
<slf4jVersion>1.6.1</slf4jVersion> <slf4jVersion>1.6.1</slf4jVersion>
<jetty9Version>9.0.4.v20130625</jetty9Version>
</properties> </properties>
<dependencies> <dependencies>
@ -85,7 +70,7 @@ under the License.
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.8.2</version> <version>4.8.2</version>
<!-- NOTE: Use compile scope for transitivity. --> <!-- NOTE: Use compile scope for transitivity. -->
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.codehaus.plexus</groupId> <groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId> <artifactId>plexus-utils</artifactId>
@ -96,34 +81,63 @@ under the License.
<groupId>org.apache.maven.its</groupId> <groupId>org.apache.maven.its</groupId>
<artifactId>maven-it-helper</artifactId> <artifactId>maven-it-helper</artifactId>
<version>2.1-SNAPSHOT</version> <version>2.1-SNAPSHOT</version>
<!-- TODO: not transitive in tests artifact, so must be left in main scope <!-- TODO: not transitive in tests artifact, so must be left in main
<scope>test</scope> scope <scope>test</scope> -->
-->
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.shared</groupId> <groupId>org.apache.maven.shared</groupId>
<artifactId>maven-verifier</artifactId> <artifactId>maven-verifier</artifactId>
<!-- TODO: not transitive in tests artifact, so must be left in main scope <!-- TODO: not transitive in tests artifact, so must be left in main
<scope>test</scope> scope <scope>test</scope> -->
-->
</dependency> </dependency>
<!-- <!-- Should be fine to introduce this here, even if we use the embedder
Should be fine to introduce this here, even if we use the embedder as it should be using an isolated classloader as it should be using an isolated classloader Used to simplify tests that
Used to simplify tests that provide a test HTTP server. provide a test HTTP server. -->
-->
<dependency> <dependency>
<groupId>org.mortbay.jetty</groupId> <groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId> <artifactId>jetty</artifactId>
<version>6.1.14</version> <version>6.1.14</version>
<!-- TODO: not transitive in tests artifact, so must be left in main scope <!-- TODO: not transitive in tests artifact, so must be left in main
<scope>test</scope> scope <scope>test</scope> -->
--> <exclusions>
<exclusion>
<artifactId>servlet-api-2.5</artifactId>
<groupId>org.mortbay.jetty</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.shared</groupId> <groupId>org.apache.maven.shared</groupId>
<artifactId>maven-shared-utils</artifactId> <artifactId>maven-shared-utils</artifactId>
<version>0.6</version> <version>0.6</version>
</dependency> </dependency>
<!-- Jetty9 server for testing -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9Version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9Version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9Version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet</artifactId>
<version>3.0.0.v201112011016</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -178,19 +192,19 @@ under the License.
<profiles> <profiles>
<profile> <profile>
<id>parallel</id> <id>parallel</id>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<parallel>classes</parallel> <parallel>classes</parallel>
<threadCount>6</threadCount> <threadCount>6</threadCount>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>
<profile> <profile>
<id>run-its</id> <id>run-its</id>
@ -241,7 +255,8 @@ under the License.
<configuration> <configuration>
<systemProperties combine.children="append"> <systemProperties combine.children="append">
<property> <property>
<!-- Pass this through to the tests (if set!) to have them pick the right repository --> <!-- Pass this through to the tests (if set!) to have them
pick the right repository -->
<name>maven.repo.local</name> <name>maven.repo.local</name>
<value>${maven.repo.local}</value> <value>${maven.repo.local}</value>
</property> </property>
@ -265,7 +280,8 @@ under the License.
<configuration> <configuration>
<systemProperties combine.children="append"> <systemProperties combine.children="append">
<property> <property>
<!-- Pass this through to the tests (if set!) to have them pick the right repository layout --> <!-- Pass this through to the tests (if set!) to have them
pick the right repository layout -->
<name>maven.repo.local.layout</name> <name>maven.repo.local.layout</name>
<value>${maven.repo.local.layout}</value> <value>${maven.repo.local.layout}</value>
</property> </property>
@ -284,8 +300,10 @@ under the License.
<configuration> <configuration>
<skip>false</skip> <skip>false</skip>
<forkMode>once</forkMode> <forkMode>once</forkMode>
<argLine>${surefireMemory} -Dcom.sun.management.jmxremote=true</argLine> <argLine>${surefireMemory}
<!-- NOTE: Maven plugins have access to the system class path so keep it clean --> -Dcom.sun.management.jmxremote=true</argLine>
<!-- NOTE: Maven plugins have access to the system class path
so keep it clean -->
<useSystemClassLoader>false</useSystemClassLoader> <useSystemClassLoader>false</useSystemClassLoader>
<systemProperties combine.children="append"> <systemProperties combine.children="append">
<property> <property>
@ -360,11 +378,14 @@ under the License.
</goals> </goals>
<configuration> <configuration>
<tasks> <tasks>
<delete dir="${mavenHome}"/> <delete dir="${mavenHome}" />
<unzip src="${project.build.directory}/maven-bin.zip" dest="${mavenHome}"> <unzip src="${project.build.directory}/maven-bin.zip"
<globmapper from="apache-maven-${mavenVersion}/*" to="*" handledirsep="true" /> dest="${mavenHome}">
<globmapper from="apache-maven-${mavenVersion}/*"
to="*" handledirsep="true" />
</unzip> </unzip>
<chmod dir="${mavenHome}/bin" perm="ugo+rx" includes="mvn,mvnDebug"/> <chmod dir="${mavenHome}/bin" perm="ugo+rx"
includes="mvn,mvnDebug" />
</tasks> </tasks>
</configuration> </configuration>
</execution> </execution>
@ -404,11 +425,13 @@ under the License.
</goals> </goals>
<configuration> <configuration>
<tasks> <tasks>
<delete dir="${mavenHome}"/> <delete dir="${mavenHome}" />
<unzip src="${mavenDistro}" dest="${mavenHome}"> <unzip src="${mavenDistro}" dest="${mavenHome}">
<regexpmapper from="^([^/]+)/(.*)$" to="\2" handledirsep="true" /> <regexpmapper from="^([^/]+)/(.*)$"
to="\2" handledirsep="true" />
</unzip> </unzip>
<chmod dir="${mavenHome}/bin" perm="ugo+rx" includes="mvn,mvnDebug"/> <chmod dir="${mavenHome}/bin" perm="ugo+rx"
includes="mvn,mvnDebug" />
</tasks> </tasks>
</configuration> </configuration>
</execution> </execution>
@ -444,11 +467,12 @@ under the License.
</goals> </goals>
<configuration> <configuration>
<tasks> <tasks>
<delete dir="${preparedMavenHome}"/> <delete dir="${preparedMavenHome}" />
<copy todir="${preparedMavenHome}" overwrite="true"> <copy todir="${preparedMavenHome}" overwrite="true">
<fileset dir="${mavenHome}"/> <fileset dir="${mavenHome}" />
</copy> </copy>
<chmod dir="${preparedMavenHome}/bin" perm="ugo+rx" includes="mvn,mvnDebug"/> <chmod dir="${preparedMavenHome}/bin" perm="ugo+rx"
includes="mvn,mvnDebug" />
</tasks> </tasks>
</configuration> </configuration>
</execution> </execution>

View File

@ -0,0 +1,199 @@
package org.apache.maven.it;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collections;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
/**
* An HTTP server that handles all requests on a given port from a specified source, optionally secured using BASIC auth by providing a username and password.
*
* The source can either be a URL or a directory. When a request is made the request is satisfied from the provided source.
*
* @author Jason van Zyl
*
*/
public class HttpServer {
private final Server server;
private final StreamSource source;
private final String username;
private final String password;
public HttpServer(int port, String username, String password, StreamSource source) {
this.username = username;
this.password = password;
this.source = source;
this.server = server(port);
}
public void start() throws Exception {
server.start();
//server.join();
}
public void stop() throws Exception {
server.stop();
}
public int port() {
return ((ServerConnector) server.getConnectors()[0]).getLocalPort();
}
private Server server(int port) {
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setMaxThreads(500);
Server server = new Server(threadPool);
server.addBean(new ScheduledExecutorScheduler());
ServerConnector http = new ServerConnector(server);
http.setPort(port);
http.setIdleTimeout(30000);
server.addConnector(http);
StreamSourceHandler handler = new StreamSourceHandler(source);
if (username != null && password != null) {
HashLoginService loginService = new HashLoginService("Test Server");
loginService.putUser(username, new Password(password), new String[] {
"user"
});
server.addBean(loginService);
ConstraintSecurityHandler security = new ConstraintSecurityHandler();
server.setHandler(security);
Constraint constraint = new Constraint();
constraint.setName("auth");
constraint.setAuthenticate(true);
constraint.setRoles(new String[] {
"user", "admin"
});
ConstraintMapping mapping = new ConstraintMapping();
mapping.setPathSpec("/*");
mapping.setConstraint(constraint);
security.setConstraintMappings(Collections.singletonList(mapping));
security.setAuthenticator(new BasicAuthenticator());
security.setLoginService(loginService);
security.setHandler(handler);
} else {
server.setHandler(handler);
}
return server;
}
public static HttpServerBuilder builder() {
return new HttpServerBuilder();
}
public static class HttpServerBuilder {
private int port;
private String username;
private String password;
private StreamSource source;
public HttpServerBuilder port(int port) {
this.port = port;
return this;
}
public HttpServerBuilder username(String username) {
this.username = username;
return this;
}
public HttpServerBuilder password(String password) {
this.password = password;
return this;
}
public HttpServerBuilder source(final String source) {
this.source = new StreamSource() {
public InputStream stream(String path) throws IOException {
return new URL(String.format("%s/%s", source, path)).openStream();
}
};
return this;
}
public HttpServerBuilder source(final File source) {
this.source = new StreamSource() {
public InputStream stream(String path) throws IOException {
return new FileInputStream(new File(source, path));
}
};
return this;
}
public HttpServer build() {
return new HttpServer(port, username, password, source);
}
}
public static interface StreamSource {
InputStream stream(String path) throws IOException;
}
public static class StreamSourceHandler extends AbstractHandler {
private final StreamSource source;
public StreamSourceHandler(StreamSource source) {
this.source = source;
}
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("application/octet-stream");
response.setStatus(HttpServletResponse.SC_OK);
Closer closer = Closer.create();
try {
// /target --> target
InputStream in = closer.register(source.stream(target.substring(1)));
OutputStream out = closer.register(response.getOutputStream());
ByteStreams.copy(in, out);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
baseRequest.setHandled(true);
}
}
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.builder() //
.port(0) //
.username("maven") //
.password("secret") //
.source(new File("/tmp/repo")) //
.build();
server.start();
}
}

View File

@ -1,6 +1,7 @@
package org.apache.maven.it; package org.apache.maven.it;
import java.io.File; import java.io.File;
import java.util.Properties;
import org.apache.maven.it.util.ResourceExtractor; import org.apache.maven.it.util.ResourceExtractor;
@ -30,6 +31,36 @@ public class MavenITmng5771CoreExtensionsTest
verifier.resetStreams(); verifier.resetStreams();
} }
public void testCoreExtensionRetrievedFromAMirrorWithBasicAuthentication()
throws Exception
{
File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/mng-5771-core-extensions" );
HttpServer server = HttpServer.builder() //
.port(0) //
.username("maven") //
.password("secret") //
.source(new File(testDir, "repo")) //
.build();
server.start();
Verifier verifier = newVerifier( testDir.getAbsolutePath() );
Properties properties = verifier.newDefaultFilterProperties();
properties.setProperty("@port@", Integer.toString(server.port()));
verifier.filterFile( "settings-template-mirror-auth.xml", "settings.xml", "UTF-8", properties );
verifier = newVerifier( new File( testDir, "client" ).getAbsolutePath() );
verifier.deleteDirectory( "target" );
verifier.deleteArtifacts( "org.apache.maven.its.it-core-extensions" );
verifier.getCliOptions().add( "-s" );
verifier.getCliOptions().add( new File( testDir, "settings.xml" ).getAbsolutePath() );
verifier.executeGoal( "validate" );
verifier.verifyErrorFreeLog();
verifier.resetStreams();
server.stop();
}
public void testCoreExtensionNoDescriptor() public void testCoreExtensionNoDescriptor()
throws Exception throws Exception
{ {

View File

@ -0,0 +1,41 @@
<settings>
<servers>
<server>
<id>repoman</id>
<username>maven</username>
<password>secret</password>
</server>
</servers>
<mirrors>
<mirror>
<id>repoman</id>
<name>Mirror</name>
<url>http://localhost:@port@</url>
<mirrorOf>external:*</mirrorOf>
</mirror>
</mirrors>
<profiles>
<profile>
<id>development</id>
<repositories>
<repository>
<id>central</id>
<url>http://central</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://central</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>development</activeProfile>
</activeProfiles>
</settings>

View File

@ -63,6 +63,8 @@ under the License.
<maven.site.path>core-its</maven.site.path> <maven.site.path>core-its</maven.site.path>
<maven.site.cache>${user.home}/maven-sites</maven.site.cache> <maven.site.cache>${user.home}/maven-sites</maven.site.cache>
<rat.ignoreErrors>true</rat.ignoreErrors> <rat.ignoreErrors>true</rat.ignoreErrors>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties> </properties>
<modules> <modules>