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 ArchivaXworkUser archivaXworkUser;
90 private RepositoryServlet servlet;
97 String appserverBase = getTestFile( "target/appserver-base" ).getAbsolutePath();
98 System.setProperty( "appserver.base", appserverBase );
100 File testConf = getTestFile( "src/test/resources/repository-archiva.xml" );
101 File testConfDest = new File( appserverBase, "conf/archiva.xml" );
102 FileUtils.copyFile( testConf, testConfDest );
104 archivaConfiguration = (ArchivaConfiguration) lookup( ArchivaConfiguration.class );
105 repoRootInternal = new File( appserverBase, "data/repositories/internal" );
106 Configuration config = archivaConfiguration.getConfiguration();
108 config.addManagedRepository( createManagedRepository( REPOID_INTERNAL, "Internal Test Repo", repoRootInternal ) );
109 saveConfiguration( archivaConfiguration );
111 CacheManager.getInstance().removeCache( "url-failures-cache" );
113 HttpUnitOptions.setExceptionsThrownOnErrorStatus( false );
115 sr = new ServletRunner( getTestFile( "src/test/resources/WEB-INF/repository-servlet-security-test/web.xml" ) );
116 sr.registerServlet( "/repository/*", RepositoryServlet.class.getName() );
119 servletAuthControl = MockControl.createControl( ServletAuthenticator.class );
120 servletAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
121 servletAuth = (ServletAuthenticator) servletAuthControl.getMock();
124 MockClassControl.createControl( HttpBasicAuthentication.class, HttpBasicAuthentication.class.getMethods() );
125 httpAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
126 httpAuth = (HttpAuthenticator) httpAuthControl.getMock();
128 archivaXworkUser = new ArchivaXworkUser();
129 archivaXworkUser.setGuest( "guest" );
131 davSessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth, archivaXworkUser );
134 protected ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location )
136 ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();
138 repo.setName( name );
139 repo.setLocation( location.getAbsolutePath() );
143 protected void saveConfiguration()
146 saveConfiguration( archivaConfiguration );
149 protected void saveConfiguration( ArchivaConfiguration archivaConfiguration )
152 archivaConfiguration.save( archivaConfiguration.getConfiguration() );
155 protected void setupCleanRepo( File repoRootDir )
158 FileUtils.deleteDirectory( repoRootDir );
159 if ( !repoRootDir.exists() )
161 repoRootDir.mkdirs();
166 protected String getPlexusConfigLocation()
168 return "org/apache/maven/archiva/webdav/RepositoryServletSecurityTest.xml";
172 protected void tearDown()
185 if ( repoRootInternal.exists() )
187 FileUtils.deleteDirectory(repoRootInternal);
195 // test deploy with invalid user, and guest has no write access to repo
196 // 401 must be returned
197 public void testPutWithInvalidUserAndGuestHasNoWriteAccess()
200 setupCleanRepo( repoRootInternal );
202 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
203 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
204 assertNotNull( "artifact.jar inputstream", is );
206 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
207 InvocationContext ic = sc.newInvocation( request );
208 servlet = (RepositoryServlet) ic.getServlet();
209 servlet.setDavSessionProvider( davSessionProvider );
211 AuthenticationResult result = new AuthenticationResult();
212 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
213 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
214 new AuthenticationException( "Authentication error" ) );
216 servletAuth.isAuthorized( "guest", "internal", true );
217 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
218 servletAuthControl.setThrowable( new UnauthorizedException( "'guest' has no write access to repository" ) );
220 httpAuthControl.replay();
221 servletAuthControl.replay();
223 servlet.service( ic.getRequest(), ic.getResponse() );
225 httpAuthControl.verify();
226 servletAuthControl.verify();
228 //assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
231 // test deploy with invalid user, but guest has write access to repo
232 public void testPutWithInvalidUserAndGuestHasWriteAccess()
235 setupCleanRepo( repoRootInternal );
237 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
238 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
239 assertNotNull( "artifact.jar inputstream", is );
241 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
243 InvocationContext ic = sc.newInvocation( request );
244 servlet = (RepositoryServlet) ic.getServlet();
245 servlet.setDavSessionProvider( davSessionProvider );
247 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
248 archivaDavResourceFactory.setHttpAuth( httpAuth );
249 archivaDavResourceFactory.setServletAuth( servletAuth );
251 servlet.setResourceFactory( archivaDavResourceFactory );
253 AuthenticationResult result = new AuthenticationResult();
254 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
255 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
256 new AuthenticationException( "Authentication error" ) );
258 servletAuth.isAuthorized( "guest", "internal", true );
259 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
260 servletAuthControl.setReturnValue( true );
262 // ArchivaDavResourceFactory#isAuthorized()
263 SecuritySession session = new DefaultSecuritySession();
264 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
265 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true) ), session );
266 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, result ),
267 new AuthenticationException( "Authentication error" ) );
269 // check if guest has write access
270 servletAuth.isAuthorized( "guest", "internal", true );
271 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
272 servletAuthControl.setReturnValue( true );
274 httpAuthControl.replay();
275 servletAuthControl.replay();
277 servlet.service( ic.getRequest(), ic.getResponse() );
279 httpAuthControl.verify();
280 servletAuthControl.verify();
282 // assertEquals( HttpServletResponse.SC_CREATED, response.getResponseCode() );
285 // test deploy with a valid user with no write access
286 public void testPutWithValidUserWithNoWriteAccess()
289 setupCleanRepo( repoRootInternal );
291 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
292 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
293 assertNotNull( "artifact.jar inputstream", is );
295 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
297 InvocationContext ic = sc.newInvocation( request );
298 servlet = (RepositoryServlet) ic.getServlet();
299 servlet.setDavSessionProvider( davSessionProvider );
301 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
302 archivaDavResourceFactory.setHttpAuth( httpAuth );
303 archivaDavResourceFactory.setServletAuth( servletAuth );
304 servlet.setResourceFactory( archivaDavResourceFactory );
306 AuthenticationResult result = new AuthenticationResult();
307 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
308 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
310 // ArchivaDavResourceFactory#isAuthorized()
311 SecuritySession session = new DefaultSecuritySession();
312 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
313 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
314 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
315 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
316 new UnauthorizedException( "User not authorized" ) );
318 httpAuthControl.replay();
319 servletAuthControl.replay();
321 servlet.service( ic.getRequest(), ic.getResponse() );
323 httpAuthControl.verify();
324 servletAuthControl.verify();
326 // assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
329 // test deploy with a valid user with write access
330 public void testPutWithValidUserWithWriteAccess()
333 setupCleanRepo( repoRootInternal );
334 assertTrue( repoRootInternal.exists() );
336 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
337 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
338 assertNotNull( "artifact.jar inputstream", is );
340 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
342 InvocationContext ic = sc.newInvocation( request );
343 servlet = (RepositoryServlet) ic.getServlet();
344 servlet.setDavSessionProvider( davSessionProvider );
346 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
347 archivaDavResourceFactory.setHttpAuth( httpAuth );
348 archivaDavResourceFactory.setServletAuth( servletAuth );
350 servlet.setResourceFactory( archivaDavResourceFactory );
352 AuthenticationResult result = new AuthenticationResult();
353 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
354 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
356 // ArchivaDavResourceFactory#isAuthorized()
357 SecuritySession session = new DefaultSecuritySession();
358 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
359 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
360 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
361 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
363 httpAuthControl.replay();
364 servletAuthControl.replay();
366 servlet.service( ic.getRequest(), ic.getResponse() );
368 httpAuthControl.verify();
369 servletAuthControl.verify();
371 // assertEquals(HttpServletResponse.SC_CREATED, response.getResponseCode());
374 // test get with invalid user, and guest has read access to repo
375 public void testGetWithInvalidUserAndGuestHasReadAccess()
378 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
379 String expectedArtifactContents = "dummy-commons-lang-artifact";
381 File artifactFile = new File( repoRootInternal, commonsLangJar );
382 artifactFile.getParentFile().mkdirs();
384 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
386 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
387 InvocationContext ic = sc.newInvocation( request );
388 servlet = (RepositoryServlet) ic.getServlet();
389 servlet.setDavSessionProvider( davSessionProvider );
391 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
392 archivaDavResourceFactory.setHttpAuth( httpAuth );
393 archivaDavResourceFactory.setServletAuth( servletAuth );
395 servlet.setResourceFactory( archivaDavResourceFactory );
397 AuthenticationResult result = new AuthenticationResult();
398 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
399 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
400 new AuthenticationException( "Authentication error" ) );
401 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), true );
403 // ArchivaDavResourceFactory#isAuthorized()
404 SecuritySession session = new DefaultSecuritySession();
405 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
406 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
407 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
408 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
410 httpAuthControl.replay();
411 servletAuthControl.replay();
413 WebResponse response = sc.getResponse( request );
415 httpAuthControl.verify();
416 servletAuthControl.verify();
418 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
419 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
422 // test get with invalid user, and guest has no read access to repo
423 public void testGetWithInvalidUserAndGuestHasNoReadAccess()
426 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
427 String expectedArtifactContents = "dummy-commons-lang-artifact";
429 File artifactFile = new File( repoRootInternal, commonsLangJar );
430 artifactFile.getParentFile().mkdirs();
432 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
434 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
435 InvocationContext ic = sc.newInvocation( request );
436 servlet = (RepositoryServlet) ic.getServlet();
437 servlet.setDavSessionProvider( davSessionProvider );
439 AuthenticationResult result = new AuthenticationResult();
440 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
441 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
442 new AuthenticationException( "Authentication error" ) );
443 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), false );
445 httpAuthControl.replay();
446 servletAuthControl.replay();
448 WebResponse response = sc.getResponse( request );
450 httpAuthControl.verify();
451 servletAuthControl.verify();
453 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );
456 // test get with valid user with read access to repo
457 public void testGetWithAValidUserWithReadAccess()
460 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
461 String expectedArtifactContents = "dummy-commons-lang-artifact";
463 File artifactFile = new File( repoRootInternal, commonsLangJar );
464 artifactFile.getParentFile().mkdirs();
466 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
468 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
469 InvocationContext ic = sc.newInvocation( request );
470 servlet = (RepositoryServlet) ic.getServlet();
471 servlet.setDavSessionProvider( davSessionProvider );
473 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
474 archivaDavResourceFactory.setHttpAuth( httpAuth );
475 archivaDavResourceFactory.setServletAuth( servletAuth );
477 servlet.setResourceFactory( archivaDavResourceFactory );
479 AuthenticationResult result = new AuthenticationResult();
480 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
481 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
483 // ArchivaDavResourceFactory#isAuthorized()
484 SecuritySession session = new DefaultSecuritySession();
485 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
486 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
487 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
488 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
490 httpAuthControl.replay();
491 servletAuthControl.replay();
493 WebResponse response = sc.getResponse( request );
495 httpAuthControl.verify();
496 servletAuthControl.verify();
498 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
499 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
502 // test get with valid user with no read access to repo
503 public void testGetWithAValidUserWithNoReadAccess()
506 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
507 String expectedArtifactContents = "dummy-commons-lang-artifact";
509 File artifactFile = new File( repoRootInternal, commonsLangJar );
510 artifactFile.getParentFile().mkdirs();
512 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
514 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
515 InvocationContext ic = sc.newInvocation( request );
516 servlet = (RepositoryServlet) ic.getServlet();
517 servlet.setDavSessionProvider( davSessionProvider );
519 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
520 archivaDavResourceFactory.setHttpAuth( httpAuth );
521 archivaDavResourceFactory.setServletAuth( servletAuth );
523 servlet.setResourceFactory( archivaDavResourceFactory );
525 AuthenticationResult result = new AuthenticationResult();
526 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
527 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
529 // ArchivaDavResourceFactory#isAuthorized()
530 SecuritySession session = new DefaultSecuritySession();
531 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
532 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
533 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
534 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
535 new UnauthorizedException( "User not authorized to read repository." ) );
537 httpAuthControl.replay();
538 servletAuthControl.replay();
540 WebResponse response = sc.getResponse( request );
542 httpAuthControl.verify();
543 servletAuthControl.verify();
545 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );