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.DavSessionProvider;
32 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
33 import org.apache.maven.archiva.configuration.Configuration;
34 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
35 import org.apache.maven.archiva.security.ArchivaXworkUser;
36 import org.apache.maven.archiva.security.ServletAuthenticator;
37 import org.codehaus.plexus.redback.authentication.AuthenticationException;
38 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
39 import org.codehaus.plexus.redback.authorization.UnauthorizedException;
40 import org.codehaus.plexus.redback.system.DefaultSecuritySession;
41 import org.codehaus.plexus.redback.system.SecuritySession;
42 import org.codehaus.plexus.spring.PlexusInSpringTestCase;
43 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
44 import org.codehaus.redback.integration.filter.authentication.basic.HttpBasicAuthentication;
45 import org.easymock.MockControl;
46 import org.easymock.classextension.MockClassControl;
48 import com.meterware.httpunit.GetMethodWebRequest;
49 import com.meterware.httpunit.HttpUnitOptions;
50 import com.meterware.httpunit.PutMethodWebRequest;
51 import com.meterware.httpunit.WebRequest;
52 import com.meterware.httpunit.WebResponse;
53 import com.meterware.servletunit.InvocationContext;
54 import com.meterware.servletunit.ServletRunner;
55 import com.meterware.servletunit.ServletUnitClient;
58 * RepositoryServletSecurityTest
60 * Test the flow of the authentication and authorization checks. This does not necessarily
61 * perform redback security checking.
65 public class RepositoryServletSecurityTest
66 extends PlexusInSpringTestCase
68 protected static final String REPOID_INTERNAL = "internal";
70 protected ServletUnitClient sc;
72 protected File repoRootInternal;
74 private ServletRunner sr;
76 protected ArchivaConfiguration archivaConfiguration;
78 private DavSessionProvider davSessionProvider;
80 private MockControl servletAuthControl;
82 private ServletAuthenticator servletAuth;
84 private MockClassControl httpAuthControl;
86 private HttpAuthenticator httpAuth;
88 private RepositoryServlet servlet;
95 String appserverBase = getTestFile( "target/appserver-base" ).getAbsolutePath();
96 System.setProperty( "appserver.base", appserverBase );
98 File testConf = getTestFile( "src/test/resources/repository-archiva.xml" );
99 File testConfDest = new File( appserverBase, "conf/archiva.xml" );
100 FileUtils.copyFile( testConf, testConfDest );
102 archivaConfiguration = (ArchivaConfiguration) lookup( ArchivaConfiguration.class );
103 repoRootInternal = new File( appserverBase, "data/repositories/internal" );
104 Configuration config = archivaConfiguration.getConfiguration();
106 config.addManagedRepository( createManagedRepository( REPOID_INTERNAL, "Internal Test Repo", repoRootInternal ) );
107 saveConfiguration( archivaConfiguration );
109 CacheManager.getInstance().removeCache( "url-failures-cache" );
111 HttpUnitOptions.setExceptionsThrownOnErrorStatus( false );
113 sr = new ServletRunner( getTestFile( "src/test/resources/WEB-INF/repository-servlet-security-test/web.xml" ) );
114 sr.registerServlet( "/repository/*", RepositoryServlet.class.getName() );
117 servletAuthControl = MockControl.createControl( ServletAuthenticator.class );
118 servletAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
119 servletAuth = (ServletAuthenticator) servletAuthControl.getMock();
122 MockClassControl.createControl( HttpBasicAuthentication.class, HttpBasicAuthentication.class.getMethods() );
123 httpAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
124 httpAuth = (HttpAuthenticator) httpAuthControl.getMock();
126 ArchivaXworkUser archivaXworkUser = (ArchivaXworkUser) lookup( ArchivaXworkUser.class );
128 davSessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth, archivaXworkUser );
131 protected ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location )
133 ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();
135 repo.setName( name );
136 repo.setLocation( location.getAbsolutePath() );
140 protected void saveConfiguration()
143 saveConfiguration( archivaConfiguration );
146 protected void saveConfiguration( ArchivaConfiguration archivaConfiguration )
149 archivaConfiguration.save( archivaConfiguration.getConfiguration() );
152 protected void setupCleanRepo( File repoRootDir )
155 FileUtils.deleteDirectory( repoRootDir );
156 if ( !repoRootDir.exists() )
158 repoRootDir.mkdirs();
163 protected String getPlexusConfigLocation()
165 return "org/apache/maven/archiva/webdav/RepositoryServletSecurityTest.xml";
169 protected void tearDown()
182 if ( repoRootInternal.exists() )
184 FileUtils.deleteDirectory(repoRootInternal);
192 // test deploy with invalid user, and guest has no write access to repo
193 // 401 must be returned
194 public void testPutWithInvalidUserAndGuestHasNoWriteAccess()
197 setupCleanRepo( repoRootInternal );
199 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
200 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
201 assertNotNull( "artifact.jar inputstream", is );
203 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
204 InvocationContext ic = sc.newInvocation( request );
205 servlet = (RepositoryServlet) ic.getServlet();
206 servlet.setDavSessionProvider( davSessionProvider );
208 AuthenticationResult result = new AuthenticationResult();
209 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
210 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
211 new AuthenticationException( "Authentication error" ) );
213 servletAuth.isAuthorized( "guest", "internal", true );
214 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
215 servletAuthControl.setThrowable( new UnauthorizedException( "'guest' has no write access to repository" ) );
217 httpAuthControl.replay();
218 servletAuthControl.replay();
220 servlet.service( ic.getRequest(), ic.getResponse() );
222 httpAuthControl.verify();
223 servletAuthControl.verify();
225 //assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
228 // test deploy with invalid user, but guest has write access to repo
229 public void testPutWithInvalidUserAndGuestHasWriteAccess()
232 setupCleanRepo( repoRootInternal );
234 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
235 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
236 assertNotNull( "artifact.jar inputstream", is );
238 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
240 InvocationContext ic = sc.newInvocation( request );
241 servlet = (RepositoryServlet) ic.getServlet();
242 servlet.setDavSessionProvider( davSessionProvider );
244 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
245 archivaDavResourceFactory.setHttpAuth( httpAuth );
246 archivaDavResourceFactory.setServletAuth( servletAuth );
248 servlet.setResourceFactory( archivaDavResourceFactory );
250 AuthenticationResult result = new AuthenticationResult();
251 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
252 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
253 new AuthenticationException( "Authentication error" ) );
255 servletAuth.isAuthorized( "guest", "internal", true );
256 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
257 servletAuthControl.setReturnValue( true );
259 // ArchivaDavResourceFactory#isAuthorized()
260 SecuritySession session = new DefaultSecuritySession();
261 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
262 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true) ), session );
263 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, result ),
264 new AuthenticationException( "Authentication error" ) );
266 // check if guest has write access
267 servletAuth.isAuthorized( "guest", "internal", true );
268 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
269 servletAuthControl.setReturnValue( true );
271 httpAuthControl.replay();
272 servletAuthControl.replay();
274 servlet.service( ic.getRequest(), ic.getResponse() );
276 httpAuthControl.verify();
277 servletAuthControl.verify();
279 // assertEquals( HttpServletResponse.SC_CREATED, response.getResponseCode() );
282 // test deploy with a valid user with no write access
283 public void testPutWithValidUserWithNoWriteAccess()
286 setupCleanRepo( repoRootInternal );
288 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
289 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
290 assertNotNull( "artifact.jar inputstream", is );
292 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
294 InvocationContext ic = sc.newInvocation( request );
295 servlet = (RepositoryServlet) ic.getServlet();
296 servlet.setDavSessionProvider( davSessionProvider );
298 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
299 archivaDavResourceFactory.setHttpAuth( httpAuth );
300 archivaDavResourceFactory.setServletAuth( servletAuth );
301 servlet.setResourceFactory( archivaDavResourceFactory );
303 AuthenticationResult result = new AuthenticationResult();
304 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
305 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
307 // ArchivaDavResourceFactory#isAuthorized()
308 SecuritySession session = new DefaultSecuritySession();
309 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
310 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
311 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
312 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
313 new UnauthorizedException( "User not authorized" ) );
315 httpAuthControl.replay();
316 servletAuthControl.replay();
318 servlet.service( ic.getRequest(), ic.getResponse() );
320 httpAuthControl.verify();
321 servletAuthControl.verify();
323 // assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
326 // test deploy with a valid user with write access
327 public void testPutWithValidUserWithWriteAccess()
330 setupCleanRepo( repoRootInternal );
331 assertTrue( repoRootInternal.exists() );
333 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
334 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
335 assertNotNull( "artifact.jar inputstream", is );
337 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
339 InvocationContext ic = sc.newInvocation( request );
340 servlet = (RepositoryServlet) ic.getServlet();
341 servlet.setDavSessionProvider( davSessionProvider );
343 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
344 archivaDavResourceFactory.setHttpAuth( httpAuth );
345 archivaDavResourceFactory.setServletAuth( servletAuth );
347 servlet.setResourceFactory( archivaDavResourceFactory );
349 AuthenticationResult result = new AuthenticationResult();
350 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
351 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
353 // ArchivaDavResourceFactory#isAuthorized()
354 SecuritySession session = new DefaultSecuritySession();
355 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
356 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
357 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
358 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
360 httpAuthControl.replay();
361 servletAuthControl.replay();
363 servlet.service( ic.getRequest(), ic.getResponse() );
365 httpAuthControl.verify();
366 servletAuthControl.verify();
368 // assertEquals(HttpServletResponse.SC_CREATED, response.getResponseCode());
371 // test get with invalid user, and guest has read access to repo
372 public void testGetWithInvalidUserAndGuestHasReadAccess()
375 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
376 String expectedArtifactContents = "dummy-commons-lang-artifact";
378 File artifactFile = new File( repoRootInternal, commonsLangJar );
379 artifactFile.getParentFile().mkdirs();
381 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
383 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
384 InvocationContext ic = sc.newInvocation( request );
385 servlet = (RepositoryServlet) ic.getServlet();
386 servlet.setDavSessionProvider( davSessionProvider );
388 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
389 archivaDavResourceFactory.setHttpAuth( httpAuth );
390 archivaDavResourceFactory.setServletAuth( servletAuth );
392 servlet.setResourceFactory( archivaDavResourceFactory );
394 AuthenticationResult result = new AuthenticationResult();
395 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
396 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
397 new AuthenticationException( "Authentication error" ) );
398 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), true );
400 // ArchivaDavResourceFactory#isAuthorized()
401 SecuritySession session = new DefaultSecuritySession();
402 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
403 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
404 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
405 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
407 httpAuthControl.replay();
408 servletAuthControl.replay();
410 WebResponse response = sc.getResponse( request );
412 httpAuthControl.verify();
413 servletAuthControl.verify();
415 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
416 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
419 // test get with invalid user, and guest has no read access to repo
420 public void testGetWithInvalidUserAndGuestHasNoReadAccess()
423 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
424 String expectedArtifactContents = "dummy-commons-lang-artifact";
426 File artifactFile = new File( repoRootInternal, commonsLangJar );
427 artifactFile.getParentFile().mkdirs();
429 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
431 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
432 InvocationContext ic = sc.newInvocation( request );
433 servlet = (RepositoryServlet) ic.getServlet();
434 servlet.setDavSessionProvider( davSessionProvider );
436 AuthenticationResult result = new AuthenticationResult();
437 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
438 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
439 new AuthenticationException( "Authentication error" ) );
440 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), false );
442 httpAuthControl.replay();
443 servletAuthControl.replay();
445 WebResponse response = sc.getResponse( request );
447 httpAuthControl.verify();
448 servletAuthControl.verify();
450 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );
453 // test get with valid user with read access to repo
454 public void testGetWithAValidUserWithReadAccess()
457 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
458 String expectedArtifactContents = "dummy-commons-lang-artifact";
460 File artifactFile = new File( repoRootInternal, commonsLangJar );
461 artifactFile.getParentFile().mkdirs();
463 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
465 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
466 InvocationContext ic = sc.newInvocation( request );
467 servlet = (RepositoryServlet) ic.getServlet();
468 servlet.setDavSessionProvider( davSessionProvider );
470 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
471 archivaDavResourceFactory.setHttpAuth( httpAuth );
472 archivaDavResourceFactory.setServletAuth( servletAuth );
474 servlet.setResourceFactory( archivaDavResourceFactory );
476 AuthenticationResult result = new AuthenticationResult();
477 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
478 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
480 // ArchivaDavResourceFactory#isAuthorized()
481 SecuritySession session = new DefaultSecuritySession();
482 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
483 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
484 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
485 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
487 httpAuthControl.replay();
488 servletAuthControl.replay();
490 WebResponse response = sc.getResponse( request );
492 httpAuthControl.verify();
493 servletAuthControl.verify();
495 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
496 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
499 // test get with valid user with no read access to repo
500 public void testGetWithAValidUserWithNoReadAccess()
503 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
504 String expectedArtifactContents = "dummy-commons-lang-artifact";
506 File artifactFile = new File( repoRootInternal, commonsLangJar );
507 artifactFile.getParentFile().mkdirs();
509 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
511 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
512 InvocationContext ic = sc.newInvocation( request );
513 servlet = (RepositoryServlet) ic.getServlet();
514 servlet.setDavSessionProvider( davSessionProvider );
516 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
517 archivaDavResourceFactory.setHttpAuth( httpAuth );
518 archivaDavResourceFactory.setServletAuth( servletAuth );
520 servlet.setResourceFactory( archivaDavResourceFactory );
522 AuthenticationResult result = new AuthenticationResult();
523 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
524 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
526 // ArchivaDavResourceFactory#isAuthorized()
527 SecuritySession session = new DefaultSecuritySession();
528 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
529 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
530 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
531 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
532 new UnauthorizedException( "User not authorized to read repository." ) );
534 httpAuthControl.replay();
535 servletAuthControl.replay();
537 WebResponse response = sc.getResponse( request );
539 httpAuthControl.verify();
540 servletAuthControl.verify();
542 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );