1 package org.apache.archiva.upload;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
21 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
22 import org.apache.archiva.admin.model.beans.RemoteRepository;
23 import org.apache.archiva.redback.rest.api.model.User;
24 import org.apache.archiva.redback.rest.api.services.RoleManagementService;
25 import org.apache.archiva.redback.rest.api.services.UserService;
26 import org.apache.archiva.redback.rest.services.AbstractRestServicesTest;
27 import org.apache.archiva.redback.rest.services.FakeCreateAdminService;
28 import org.apache.archiva.remotedownload.AbstractDownloadTest;
29 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
30 import org.apache.archiva.security.common.ArchivaRoleConstants;
31 import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner;
32 import org.apache.archiva.web.api.FileUploadService;
33 import org.apache.archiva.web.api.RuntimeInfoService;
34 import org.apache.archiva.web.model.ApplicationRuntimeInfo;
35 import org.apache.archiva.web.model.FileMetadata;
36 import org.apache.catalina.Context;
37 import org.apache.catalina.LifecycleException;
38 import org.apache.catalina.deploy.ApplicationParameter;
39 import org.apache.catalina.startup.Tomcat;
40 import org.apache.commons.io.FileUtils;
41 import org.apache.commons.lang.StringUtils;
42 import org.apache.commons.lang.SystemUtils;
43 import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
44 import org.apache.cxf.jaxrs.client.WebClient;
45 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
46 import org.apache.cxf.jaxrs.ext.multipart.AttachmentBuilder;
47 import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
48 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
49 import org.apache.cxf.message.Message;
50 import org.apache.cxf.transport.servlet.CXFServlet;
51 import org.apache.maven.wagon.providers.http.HttpWagon;
52 import org.apache.maven.wagon.repository.Repository;
53 import org.eclipse.jetty.server.Server;
54 import org.eclipse.jetty.servlet.ServletContextHandler;
55 import org.eclipse.jetty.servlet.ServletHolder;
56 import org.junit.After;
57 import org.junit.AfterClass;
58 import org.junit.Before;
59 import org.junit.BeforeClass;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64 import org.slf4j.bridge.SLF4JBridgeHandler;
65 import org.springframework.web.context.ContextLoaderListener;
67 import javax.servlet.ServletException;
68 import javax.servlet.http.HttpServlet;
69 import javax.servlet.http.HttpServletRequest;
70 import javax.servlet.http.HttpServletResponse;
72 import java.io.IOException;
73 import java.net.URLEncoder;
74 import java.nio.file.Files;
75 import java.nio.file.Path;
76 import java.nio.file.Paths;
77 import java.nio.file.StandardCopyOption;
78 import java.util.Collections;
79 import java.util.List;
80 import java.util.zip.ZipEntry;
81 import java.util.zip.ZipFile;
84 * @author Olivier Lamy
86 @RunWith( ArchivaBlockJUnit4ClassRunner.class )
87 public class UploadArtifactsTest
88 extends AbstractRestServicesTest
90 private Tomcat tomcat;
94 public void startServer( )
97 System.setProperty( "org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true" );
98 System.setProperty("appserver.base", Paths.get("target/appserver-base").toAbsolutePath().toString());
99 Path appServerBase = Paths.get("target/appserver-base");
100 FileUtils.deleteDirectory(appServerBase.toAbsolutePath().toFile());
101 Path confDir = appServerBase.resolve( "conf" );
102 if ( !Files.exists(confDir))
104 Files.createDirectories(confDir);
106 Path log4jCfg = Paths.get( "src/test/resources/log4j2-test.xml" );
107 Path log4jCfgDst = confDir.resolve( log4jCfg.getFileName( ) );
109 Files.copy( log4jCfg, log4jCfgDst, StandardCopyOption.REPLACE_EXISTING );
111 Path archivaCfg = Paths.get( "src/test/resources/archiva.xml" );
112 Files.copy( archivaCfg, confDir.resolve( archivaCfg.getFileName( ) ), StandardCopyOption.REPLACE_EXISTING );
114 Path jcrDirectory = appServerBase.resolve("jcr" );
116 if ( Files.exists(jcrDirectory) )
118 FileUtils.deleteDirectory( jcrDirectory.toAbsolutePath().toFile() );
120 // We have to activate this to verify the bad path traversal protection. We cannot rely on
121 // the application server only.
123 SLF4JBridgeHandler.removeHandlersForRootLogger();
124 SLF4JBridgeHandler.install();
125 this.tomcat = new Tomcat();
126 this.tomcat.setBaseDir(System.getProperty("java.io.tmpdir"));
127 this.tomcat.setPort(0);
128 this.tomcat.setSilent(false);
129 Context context = this.tomcat.addContext("", System.getProperty("java.io.tmpdir"));
130 ApplicationParameter applicationParameter = new ApplicationParameter();
131 applicationParameter.setName("contextConfigLocation");
132 applicationParameter.setValue(this.getSpringConfigLocation());
133 context.addApplicationParameter(applicationParameter);
134 context.addApplicationListener(ContextLoaderListener.class.getName());
135 Tomcat.addServlet(context, "cxf", new CXFServlet());
136 context.addServletMapping("/" + this.getRestServicesPath() + "/*", "cxf");
138 this.port = this.tomcat.getConnector().getLocalPort();
139 this.log.info("start server on port {}", this.port);
140 UserService userService = this.getUserService();
141 User adminUser = new User();
142 adminUser.setUsername("admin");
143 adminUser.setPassword("rose210208");
144 adminUser.setFullName("the admin user");
145 adminUser.setEmail("toto@toto.fr");
146 userService.createAdminUser(adminUser);
147 FakeCreateAdminService fakeCreateAdminService = this.getFakeCreateAdminService();
148 // super.startServer( );
152 public void stop( ) {
153 if (this.tomcat != null) {
156 } catch (LifecycleException e) {
160 System.clearProperty( "org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH" );
164 protected String getSpringConfigLocation( )
166 return "classpath*:META-INF/spring-context.xml,classpath:/spring-context-test-upload.xml";
170 protected String getRestServicesPath( )
172 return "restServices";
175 protected String getBaseUrl( )
177 String baseUrlSysProps = System.getProperty( "archiva.baseRestUrl" );
178 return StringUtils.isBlank( baseUrlSysProps ) ? "http://localhost:" + port : baseUrlSysProps;
181 private FileUploadService getUploadService( )
183 FileUploadService service =
184 JAXRSClientFactory.create( getBaseUrl( ) + "/" + getRestServicesPath( ) + "/archivaUiServices/",
185 FileUploadService.class,
186 Collections.singletonList( new JacksonJaxbJsonProvider( ) ) );
187 log.debug( "Service class {}", service.getClass( ).getName( ) );
188 WebClient.client( service ).header( "Authorization", authorizationHeader );
189 WebClient.client( service ).header( "Referer", "http://localhost:" + port );
191 WebClient.client( service ).header( "Referer", "http://localhost" );
192 WebClient.getConfig( service ).getRequestContext( ).put( Message.MAINTAIN_SESSION, true );
193 WebClient.getConfig( service).getRequestContext().put(Message.EXCEPTION_MESSAGE_CAUSE_ENABLED, true);
194 WebClient.getConfig( service).getRequestContext().put(Message.FAULT_STACKTRACE_ENABLED, true);
195 WebClient.getConfig( service).getRequestContext().put(Message.PROPOGATE_EXCEPTION, true);
196 WebClient.getConfig( service).getRequestContext().put("org.apache.cxf.transport.no_io_exceptions", true);
198 // WebClient.client( service ).
203 public void clearUploadedFiles( )
206 FileUploadService service = getUploadService( );
207 service.clearUploadedFiles( );
211 public void uploadFile( ) throws IOException, ArchivaRestServiceException
213 FileUploadService service = getUploadService( );
216 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
217 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"" + file.getFileName( ).toString( ) + "\"; name=\"files[]\"" ) ).build( );
218 MultipartBody body = new MultipartBody( fileAttachment );
219 service.post( body );
223 service.clearUploadedFiles( );
228 public void failUploadFileWithBadFileName( ) throws IOException, ArchivaRestServiceException
230 FileUploadService service = getUploadService( );
233 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
234 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"/../TestFile.testext\"; name=\"files[]\"" ) ).build( );
235 MultipartBody body = new MultipartBody( fileAttachment );
238 service.post( body );
239 fail( "FileNames with path contents should not be allowed." );
241 catch ( ArchivaRestServiceException e )
248 service.clearUploadedFiles( );
253 public void uploadAndDeleteFile( ) throws IOException, ArchivaRestServiceException
255 FileUploadService service = getUploadService( );
258 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
259 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"" + file.getFileName( ).toString( ) + "\"; name=\"files[]\"" ) ).build( );
260 MultipartBody body = new MultipartBody( fileAttachment );
261 service.post( body );
262 service.deleteFile( file.getFileName( ).toString( ) );
266 service.clearUploadedFiles( );
271 public void failUploadAndDeleteWrongFile( ) throws IOException, ArchivaRestServiceException
273 FileUploadService service = getUploadService( );
276 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
277 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"" + file.getFileName( ).toString( ) + "\"; name=\"files[]\"" ) ).build( );
278 MultipartBody body = new MultipartBody( fileAttachment );
279 service.post( body );
280 assertFalse( service.deleteFile( "file123" + file.getFileName( ).toString( ) ) );
284 service.clearUploadedFiles( );
289 public void failUploadAndDeleteFileInOtherDir( ) throws IOException, ArchivaRestServiceException
291 Path testFile = null;
294 FileUploadService service = getUploadService( );
295 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
296 Path targetDir = Paths.get( "target/testDelete" ).toAbsolutePath( );
297 if ( !Files.exists( targetDir ) ) Files.createDirectories( targetDir );
298 Path tempDir = SystemUtils.getJavaIoTmpDir( ).toPath( );
299 testFile = Files.createTempFile( targetDir, "TestFile", ".txt" );
300 log.debug( "Test file {}", testFile.toAbsolutePath( ) );
301 log.debug( "Tmp dir {}", tempDir.toAbsolutePath( ) );
302 assertTrue( Files.exists( testFile ) );
303 Path relativePath = tempDir.relativize( testFile.toAbsolutePath( ) );
304 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"" + file.getFileName( ).toString( ) + "\"; name=\"files[]\"" ) ).build( );
305 MultipartBody body = new MultipartBody( fileAttachment );
306 service.post( body );
307 String relativePathEncoded = URLEncoder.encode( "../target/" + relativePath.toString( ), "UTF-8" );
308 log.debug( "Trying to delete with path traversal: {}, {}", relativePath, relativePathEncoded );
311 service.deleteFile( relativePathEncoded );
313 catch ( ArchivaRestServiceException ex )
315 // Expected exception
317 assertTrue( "File in another directory may not be deleted", Files.exists( testFile ) );
321 if ( testFile != null )
323 Files.deleteIfExists( testFile );
329 public void failSaveFileWithBadParams( ) throws IOException, ArchivaRestServiceException
331 FileUploadService service = getUploadService( );
332 Path targetFile = Paths.get( "target/test/test-testSave.4" );
333 Path targetPom = Paths.get( "target/test/test-testSave.pom" );
336 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
338 Path targetDir = Paths.get( "target/appserver-base/test/testSave" ).toAbsolutePath( );
339 Path repoDir = Paths.get("target/appserver-base/repositories/internal/org");
340 log.info("Repo dir {}", repoDir.toAbsolutePath());
341 if (!Files.exists(repoDir)) Files.createDirectories(repoDir);
342 assertTrue(Files.exists(repoDir));
343 if ( !Files.exists( targetDir ) ) Files.createDirectories( targetDir );
344 Files.deleteIfExists( targetFile );
345 Files.deleteIfExists( targetPom );
346 Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"archiva-model-1.2.jar\"; name=\"files[]\"" ) ).build( );
347 MultipartBody body = new MultipartBody( fileAttachment );
348 FileMetadata meta = service.post( body );
349 log.debug( "Metadata {}", meta.toString( ) );
350 assertTrue( service.save( "internal", "org.archiva", "archiva-model", "1.2", "jar", true ) );
352 fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"/../TestFile.FileExt\"; name=\"files[]\"" ) ).build( );
353 body = new MultipartBody( fileAttachment );
354 meta = service.post( body );
355 log.debug( "Metadata {}", meta.toString( ) );
357 service.save("internal", "org", URLEncoder.encode("../../../test", "UTF-8"), URLEncoder.encode("testSave", "UTF-8"), "4", true);
358 fail("Error expected, if the content contains bad characters.");
359 } catch (ArchivaRestServiceException e) {
362 assertFalse( Files.exists( Paths.get( "target/test-testSave.4" ) ) );
366 // service.clearUploadedFiles( );
367 Files.deleteIfExists( targetFile );
368 Files.deleteIfExists( targetPom );
373 public void saveFile( ) throws IOException, ArchivaRestServiceException
376 Path path = Paths.get("target/appserver-base/repositories/internal/data/repositories/internal/org/apache/archiva/archiva-model/1.2/archiva-model-1.2.jar");
377 Files.deleteIfExists( path );
378 FileUploadService service = getUploadService( );
379 Path file = Paths.get( "src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar" );
380 final Attachment fileAttachment = new AttachmentBuilder( ).object( Files.newInputStream( file ) ).contentDisposition( new ContentDisposition( "form-data; filename=\"archiva-model.jar\"; name=\"files[]\"" ) ).build( );
381 MultipartBody body = new MultipartBody( fileAttachment );
382 service.post( body );
383 service.save( "internal", "org.apache.archiva", "archiva-model", "1.2", "jar", true );