1 package org.apache.maven.archiva.webdav;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
23 import java.io.IOException;
24 import java.io.InputStream;
26 import javax.servlet.http.HttpServletResponse;
28 import net.sf.ehcache.CacheManager;
30 import org.apache.commons.io.FileUtils;
31 import org.apache.jackrabbit.webdav.DavResourceFactory;
32 import org.apache.jackrabbit.webdav.DavSessionProvider;
33 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
34 import org.apache.maven.archiva.configuration.Configuration;
35 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
36 import org.apache.maven.archiva.security.ArchivaXworkUser;
37 import org.apache.maven.archiva.security.ServletAuthenticator;
38 import org.codehaus.plexus.redback.authentication.AuthenticationException;
39 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
40 import org.codehaus.plexus.redback.authorization.UnauthorizedException;
41 import org.codehaus.plexus.redback.system.DefaultSecuritySession;
42 import org.codehaus.plexus.redback.system.SecuritySession;
43 import org.codehaus.plexus.redback.xwork.filter.authentication.HttpAuthenticator;
44 import org.codehaus.plexus.redback.xwork.filter.authentication.basic.HttpBasicAuthentication;
45 import org.codehaus.plexus.spring.PlexusInSpringTestCase;
46 import org.easymock.MockControl;
47 import org.easymock.classextension.MockClassControl;
48 import org.easymock.internal.AlwaysMatcher;
50 import com.meterware.httpunit.GetMethodWebRequest;
51 import com.meterware.httpunit.HttpUnitOptions;
52 import com.meterware.httpunit.PutMethodWebRequest;
53 import com.meterware.httpunit.WebRequest;
54 import com.meterware.httpunit.WebResponse;
55 import com.meterware.servletunit.InvocationContext;
56 import com.meterware.servletunit.ServletRunner;
57 import com.meterware.servletunit.ServletUnitClient;
60 * RepositoryServletSecurityTest
62 * Test the flow of the authentication and authorization checks. This does not necessarily
63 * perform redback security checking.
65 * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
68 public class RepositoryServletSecurityTest
69 extends PlexusInSpringTestCase
71 protected static final String REPOID_INTERNAL = "internal";
73 protected ServletUnitClient sc;
75 protected File repoRootInternal;
77 private ServletRunner sr;
79 protected ArchivaConfiguration archivaConfiguration;
81 private DavSessionProvider davSessionProvider;
83 private MockControl servletAuthControl;
85 private ServletAuthenticator servletAuth;
87 private MockClassControl httpAuthControl;
89 private HttpAuthenticator httpAuth;
91 private ArchivaXworkUser archivaXworkUser;
93 private RepositoryServlet servlet;
95 private MockControl davResourceFactoryControl;
97 private DavResourceFactory davResourceFactory;
104 String appserverBase = getTestFile( "target/appserver-base" ).getAbsolutePath();
105 System.setProperty( "appserver.base", appserverBase );
107 File testConf = getTestFile( "src/test/resources/repository-archiva.xml" );
108 File testConfDest = new File( appserverBase, "conf/archiva.xml" );
109 FileUtils.copyFile( testConf, testConfDest );
111 archivaConfiguration = (ArchivaConfiguration) lookup( ArchivaConfiguration.class );
112 repoRootInternal = new File( appserverBase, "data/repositories/internal" );
113 Configuration config = archivaConfiguration.getConfiguration();
115 config.addManagedRepository( createManagedRepository( REPOID_INTERNAL, "Internal Test Repo", repoRootInternal ) );
116 saveConfiguration( archivaConfiguration );
118 CacheManager.getInstance().removeCache( "url-failures-cache" );
120 HttpUnitOptions.setExceptionsThrownOnErrorStatus( false );
122 sr = new ServletRunner( getTestFile( "src/test/resources/WEB-INF/repository-servlet-security-test/web.xml" ) );
123 sr.registerServlet( "/repository/*", RepositoryServlet.class.getName() );
126 servletAuthControl = MockControl.createControl( ServletAuthenticator.class );
127 servletAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
128 servletAuth = (ServletAuthenticator) servletAuthControl.getMock();
131 MockClassControl.createControl( HttpBasicAuthentication.class, HttpBasicAuthentication.class.getMethods() );
132 httpAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
133 httpAuth = (HttpAuthenticator) httpAuthControl.getMock();
135 archivaXworkUser = new ArchivaXworkUser();
136 archivaXworkUser.setGuest( "guest" );
138 davSessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth, archivaXworkUser );
140 davResourceFactoryControl = MockControl.createControl( DavResourceFactory.class );
141 davResourceFactoryControl.setDefaultMatcher( new AlwaysMatcher() );
142 davResourceFactory = (DavResourceFactory) davResourceFactoryControl.getMock();
145 protected ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location )
147 ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();
149 repo.setName( name );
150 repo.setLocation( location.getAbsolutePath() );
154 protected void saveConfiguration()
157 saveConfiguration( archivaConfiguration );
160 protected void saveConfiguration( ArchivaConfiguration archivaConfiguration )
163 archivaConfiguration.save( archivaConfiguration.getConfiguration() );
166 protected void setupCleanRepo( File repoRootDir )
169 FileUtils.deleteDirectory( repoRootDir );
170 if ( !repoRootDir.exists() )
172 repoRootDir.mkdirs();
177 protected String getPlexusConfigLocation()
179 return "org/apache/maven/archiva/webdav/RepositoryServletSecurityTest.xml";
183 protected void tearDown()
196 if ( repoRootInternal.exists() )
198 FileUtils.deleteDirectory(repoRootInternal);
206 // test deploy with invalid user, and guest has no write access to repo
207 // 401 must be returned
208 public void testPutWithInvalidUserAndGuestHasNoWriteAccess()
211 setupCleanRepo( repoRootInternal );
213 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
214 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
215 assertNotNull( "artifact.jar inputstream", is );
217 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
218 InvocationContext ic = sc.newInvocation( request );
219 servlet = (RepositoryServlet) ic.getServlet();
220 servlet.setDavSessionProvider( davSessionProvider );
222 AuthenticationResult result = new AuthenticationResult();
223 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
224 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
225 new AuthenticationException( "Authentication error" ) );
227 servletAuth.isAuthorized( "guest", "internal", true );
228 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
229 servletAuthControl.setThrowable( new UnauthorizedException( "'guest' has no write access to repository" ) );
231 httpAuthControl.replay();
232 servletAuthControl.replay();
234 //WebResponse response = sc.getResponse( request );
235 servlet.service( ic.getRequest(), ic.getResponse() );
237 httpAuthControl.verify();
238 servletAuthControl.verify();
240 //assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
243 // test deploy with invalid user, but guest has write access to repo
244 public void testPutWithInvalidUserAndGuestHasWriteAccess()
247 setupCleanRepo( repoRootInternal );
249 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
250 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
251 assertNotNull( "artifact.jar inputstream", is );
253 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
255 InvocationContext ic = sc.newInvocation( request );
256 servlet = (RepositoryServlet) ic.getServlet();
257 servlet.setDavSessionProvider( davSessionProvider );
259 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
260 archivaDavResourceFactory.setHttpAuth( httpAuth );
261 archivaDavResourceFactory.setServletAuth( servletAuth );
263 servlet.setResourceFactory( archivaDavResourceFactory );
265 AuthenticationResult result = new AuthenticationResult();
266 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
267 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
268 new AuthenticationException( "Authentication error" ) );
270 servletAuth.isAuthorized( "guest", "internal", true );
271 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
272 servletAuthControl.setReturnValue( true );
273 //servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", true ), true );
275 // ArchivaDavResourceFactory#isAuthorized()
276 SecuritySession session = new DefaultSecuritySession();
277 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
278 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession(), session );
279 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, result ),
280 new AuthenticationException( "Authentication error" ) );
282 // check if guest has write access
283 servletAuth.isAuthorized( "guest", "internal", true );
284 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
285 servletAuthControl.setReturnValue( true );
286 //servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", true ), true );
288 httpAuthControl.replay();
289 servletAuthControl.replay();
291 //WebResponse response = sc.getResponse( request );
292 servlet.service( ic.getRequest(), ic.getResponse() );
294 httpAuthControl.verify();
295 servletAuthControl.verify();
297 // assertEquals( HttpServletResponse.SC_CREATED, response.getResponseCode() );
300 // test deploy with a valid user with no write access
301 public void testPutWithValidUserWithNoWriteAccess()
304 setupCleanRepo( repoRootInternal );
306 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
307 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
308 assertNotNull( "artifact.jar inputstream", is );
310 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
312 InvocationContext ic = sc.newInvocation( request );
313 servlet = (RepositoryServlet) ic.getServlet();
314 servlet.setDavSessionProvider( davSessionProvider );
316 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
317 archivaDavResourceFactory.setHttpAuth( httpAuth );
318 archivaDavResourceFactory.setServletAuth( servletAuth );
319 servlet.setResourceFactory( archivaDavResourceFactory );
321 AuthenticationResult result = new AuthenticationResult();
322 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
323 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
325 // ArchivaDavResourceFactory#isAuthorized()
326 SecuritySession session = new DefaultSecuritySession();
327 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
328 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession(), session );
329 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
330 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
331 new UnauthorizedException( "User not authorized" ) );
333 httpAuthControl.replay();
334 servletAuthControl.replay();
336 //WebResponse response = sc.getResponse( request );
337 servlet.service( ic.getRequest(), ic.getResponse() );
339 httpAuthControl.verify();
340 servletAuthControl.verify();
342 // assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
345 // test deploy with a valid user with write access
346 public void testPutWithValidUserWithWriteAccess()
349 setupCleanRepo( repoRootInternal );
350 assertTrue( repoRootInternal.exists() );
352 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
353 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
354 assertNotNull( "artifact.jar inputstream", is );
356 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
358 InvocationContext ic = sc.newInvocation( request );
359 servlet = (RepositoryServlet) ic.getServlet();
360 servlet.setDavSessionProvider( davSessionProvider );
362 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
363 archivaDavResourceFactory.setHttpAuth( httpAuth );
364 archivaDavResourceFactory.setServletAuth( servletAuth );
366 servlet.setResourceFactory( archivaDavResourceFactory );
368 AuthenticationResult result = new AuthenticationResult();
369 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
370 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
372 // ArchivaDavResourceFactory#isAuthorized()
373 SecuritySession session = new DefaultSecuritySession();
374 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
375 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession(), session );
376 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
377 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
379 httpAuthControl.replay();
380 servletAuthControl.replay();
382 // WebResponse response = sc.getResponse( request );
383 // WebResponse response = ic.getServletResponse();
385 servlet.service( ic.getRequest(), ic.getResponse() );
387 httpAuthControl.verify();
388 servletAuthControl.verify();
390 // assertEquals(HttpServletResponse.SC_CREATED, response.getResponseCode());
393 // test get with invalid user, and guest has read access to repo
394 public void testGetWithInvalidUserAndGuestHasReadAccess()
397 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
398 String expectedArtifactContents = "dummy-commons-lang-artifact";
400 File artifactFile = new File( repoRootInternal, commonsLangJar );
401 artifactFile.getParentFile().mkdirs();
403 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
405 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
406 InvocationContext ic = sc.newInvocation( request );
407 servlet = (RepositoryServlet) ic.getServlet();
408 servlet.setDavSessionProvider( davSessionProvider );
410 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
411 archivaDavResourceFactory.setHttpAuth( httpAuth );
412 archivaDavResourceFactory.setServletAuth( servletAuth );
414 servlet.setResourceFactory( archivaDavResourceFactory );
416 AuthenticationResult result = new AuthenticationResult();
417 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
418 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
419 new AuthenticationException( "Authentication error" ) );
420 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), true );
422 // ArchivaDavResourceFactory#isAuthorized()
423 SecuritySession session = new DefaultSecuritySession();
424 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
425 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession(), session );
426 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
427 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
429 httpAuthControl.replay();
430 servletAuthControl.replay();
432 WebResponse response = sc.getResponse( request );
434 httpAuthControl.verify();
435 servletAuthControl.verify();
437 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
438 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
441 // test get with invalid user, and guest has no read access to repo
442 public void testGetWithInvalidUserAndGuestHasNoReadAccess()
445 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
446 String expectedArtifactContents = "dummy-commons-lang-artifact";
448 File artifactFile = new File( repoRootInternal, commonsLangJar );
449 artifactFile.getParentFile().mkdirs();
451 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
453 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
454 InvocationContext ic = sc.newInvocation( request );
455 servlet = (RepositoryServlet) ic.getServlet();
456 servlet.setDavSessionProvider( davSessionProvider );
458 AuthenticationResult result = new AuthenticationResult();
459 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
460 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
461 new AuthenticationException( "Authentication error" ) );
462 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), false );
464 httpAuthControl.replay();
465 servletAuthControl.replay();
467 WebResponse response = sc.getResponse( request );
469 httpAuthControl.verify();
470 servletAuthControl.verify();
472 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );
475 // test get with valid user with read access to repo
476 public void testGetWithAValidUserWithReadAccess()
479 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
480 String expectedArtifactContents = "dummy-commons-lang-artifact";
482 File artifactFile = new File( repoRootInternal, commonsLangJar );
483 artifactFile.getParentFile().mkdirs();
485 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
487 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
488 InvocationContext ic = sc.newInvocation( request );
489 servlet = (RepositoryServlet) ic.getServlet();
490 servlet.setDavSessionProvider( davSessionProvider );
492 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
493 archivaDavResourceFactory.setHttpAuth( httpAuth );
494 archivaDavResourceFactory.setServletAuth( servletAuth );
496 servlet.setResourceFactory( archivaDavResourceFactory );
498 AuthenticationResult result = new AuthenticationResult();
499 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
500 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
502 // ArchivaDavResourceFactory#isAuthorized()
503 SecuritySession session = new DefaultSecuritySession();
504 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
505 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession(), session );
506 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
507 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
509 httpAuthControl.replay();
510 servletAuthControl.replay();
512 WebResponse response = sc.getResponse( request );
514 httpAuthControl.verify();
515 servletAuthControl.verify();
517 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
518 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
521 // test get with valid user with no read access to repo
522 public void testGetWithAValidUserWithNoReadAccess()
525 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
526 String expectedArtifactContents = "dummy-commons-lang-artifact";
528 File artifactFile = new File( repoRootInternal, commonsLangJar );
529 artifactFile.getParentFile().mkdirs();
531 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
533 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
534 InvocationContext ic = sc.newInvocation( request );
535 servlet = (RepositoryServlet) ic.getServlet();
536 servlet.setDavSessionProvider( davSessionProvider );
537 servlet.setResourceFactory( davResourceFactory );
539 AuthenticationResult result = new AuthenticationResult();
540 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
541 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
543 //TODO remove davResourceFactoryControl!
544 davResourceFactoryControl.expectAndThrow( davResourceFactory.createResource( null, null, null ),
545 new UnauthorizedDavException( "internal", "User not authorized" ) );
547 httpAuthControl.replay();
548 servletAuthControl.replay();
549 davResourceFactoryControl.replay();
551 WebResponse response = sc.getResponse( request );
553 httpAuthControl.verify();
554 servletAuthControl.verify();
555 davResourceFactoryControl.verify();
557 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );