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.ArchivaRoleConstants;
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.users.memory.SimpleUser;
44 import org.codehaus.plexus.spring.PlexusInSpringTestCase;
45 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
46 import org.codehaus.redback.integration.filter.authentication.basic.HttpBasicAuthentication;
47 import org.easymock.MockControl;
48 import org.easymock.classextension.MockClassControl;
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 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", ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD );
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", ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD );
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 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), null );
268 // check if guest has write access
269 servletAuth.isAuthorized( "guest", "internal", ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD );
270 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
271 servletAuthControl.setReturnValue( true );
273 httpAuthControl.replay();
274 servletAuthControl.replay();
276 servlet.service( ic.getRequest(), ic.getResponse() );
278 httpAuthControl.verify();
279 servletAuthControl.verify();
281 // assertEquals( HttpServletResponse.SC_CREATED, response.getResponseCode() );
284 // test deploy with a valid user with no write access
285 public void testPutWithValidUserWithNoWriteAccess()
288 setupCleanRepo( repoRootInternal );
290 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
291 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
292 assertNotNull( "artifact.jar inputstream", is );
294 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
296 InvocationContext ic = sc.newInvocation( request );
297 servlet = (RepositoryServlet) ic.getServlet();
298 servlet.setDavSessionProvider( davSessionProvider );
300 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
301 archivaDavResourceFactory.setHttpAuth( httpAuth );
302 archivaDavResourceFactory.setServletAuth( servletAuth );
303 servlet.setResourceFactory( archivaDavResourceFactory );
305 AuthenticationResult result = new AuthenticationResult();
306 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
307 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
309 // ArchivaDavResourceFactory#isAuthorized()
310 SecuritySession session = new DefaultSecuritySession();
311 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
312 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
313 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), new SimpleUser() );
314 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
315 servletAuthControl.expectAndThrow(
316 servletAuth.isAuthorized( null, session, "internal",
317 ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD ),
318 new UnauthorizedException( "User not authorized" ) );
320 httpAuthControl.replay();
321 servletAuthControl.replay();
323 servlet.service( ic.getRequest(), ic.getResponse() );
325 httpAuthControl.verify();
326 servletAuthControl.verify();
328 // assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
331 // test deploy with a valid user with write access
332 public void testPutWithValidUserWithWriteAccess()
335 setupCleanRepo( repoRootInternal );
336 assertTrue( repoRootInternal.exists() );
338 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
339 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
340 assertNotNull( "artifact.jar inputstream", is );
342 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
344 InvocationContext ic = sc.newInvocation( request );
345 servlet = (RepositoryServlet) ic.getServlet();
346 servlet.setDavSessionProvider( davSessionProvider );
348 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
349 archivaDavResourceFactory.setHttpAuth( httpAuth );
350 archivaDavResourceFactory.setServletAuth( servletAuth );
352 servlet.setResourceFactory( archivaDavResourceFactory );
354 AuthenticationResult result = new AuthenticationResult();
355 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
356 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
358 // ArchivaDavResourceFactory#isAuthorized()
359 SecuritySession session = new DefaultSecuritySession();
360 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
361 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
362 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), new SimpleUser() );
363 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
364 servletAuthControl.expectAndReturn(
365 servletAuth.isAuthorized( null, session, "internal",
366 ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD ),
369 httpAuthControl.replay();
370 servletAuthControl.replay();
372 servlet.service( ic.getRequest(), ic.getResponse() );
374 httpAuthControl.verify();
375 servletAuthControl.verify();
377 // assertEquals(HttpServletResponse.SC_CREATED, response.getResponseCode());
380 // test get with invalid user, and guest has read access to repo
381 public void testGetWithInvalidUserAndGuestHasReadAccess()
384 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
385 String expectedArtifactContents = "dummy-commons-lang-artifact";
387 File artifactFile = new File( repoRootInternal, commonsLangJar );
388 artifactFile.getParentFile().mkdirs();
390 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
392 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
393 InvocationContext ic = sc.newInvocation( request );
394 servlet = (RepositoryServlet) ic.getServlet();
395 servlet.setDavSessionProvider( davSessionProvider );
397 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
398 archivaDavResourceFactory.setHttpAuth( httpAuth );
399 archivaDavResourceFactory.setServletAuth( servletAuth );
401 servlet.setResourceFactory( archivaDavResourceFactory );
403 AuthenticationResult result = new AuthenticationResult();
404 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
405 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
406 new AuthenticationException( "Authentication error" ) );
407 servletAuthControl.expectAndReturn(
408 servletAuth.isAuthorized( "guest", "internal",
409 ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS ),
412 // ArchivaDavResourceFactory#isAuthorized()
413 SecuritySession session = new DefaultSecuritySession();
414 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
415 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
416 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), null );
417 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
418 servletAuthControl.expectAndReturn(
419 servletAuth.isAuthorized( null, session, "internal",
420 ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD ),
423 httpAuthControl.replay();
424 servletAuthControl.replay();
426 WebResponse response = sc.getResponse( request );
428 httpAuthControl.verify();
429 servletAuthControl.verify();
431 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
432 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
435 // test get with invalid user, and guest has no read access to repo
436 public void testGetWithInvalidUserAndGuestHasNoReadAccess()
439 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
440 String expectedArtifactContents = "dummy-commons-lang-artifact";
442 File artifactFile = new File( repoRootInternal, commonsLangJar );
443 artifactFile.getParentFile().mkdirs();
445 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
447 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
448 InvocationContext ic = sc.newInvocation( request );
449 servlet = (RepositoryServlet) ic.getServlet();
450 servlet.setDavSessionProvider( davSessionProvider );
452 AuthenticationResult result = new AuthenticationResult();
453 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
454 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
455 new AuthenticationException( "Authentication error" ) );
456 servletAuthControl.expectAndReturn(
457 servletAuth.isAuthorized( "guest", "internal",
458 ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS ),
461 httpAuthControl.replay();
462 servletAuthControl.replay();
464 WebResponse response = sc.getResponse( request );
466 httpAuthControl.verify();
467 servletAuthControl.verify();
469 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );
472 // test get with valid user with read access to repo
473 public void testGetWithAValidUserWithReadAccess()
476 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
477 String expectedArtifactContents = "dummy-commons-lang-artifact";
479 File artifactFile = new File( repoRootInternal, commonsLangJar );
480 artifactFile.getParentFile().mkdirs();
482 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
484 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
485 InvocationContext ic = sc.newInvocation( request );
486 servlet = (RepositoryServlet) ic.getServlet();
487 servlet.setDavSessionProvider( davSessionProvider );
489 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
490 archivaDavResourceFactory.setHttpAuth( httpAuth );
491 archivaDavResourceFactory.setServletAuth( servletAuth );
493 servlet.setResourceFactory( archivaDavResourceFactory );
495 AuthenticationResult result = new AuthenticationResult();
496 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
497 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
499 // ArchivaDavResourceFactory#isAuthorized()
500 SecuritySession session = new DefaultSecuritySession();
501 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
502 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
503 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), new SimpleUser() );
504 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
505 servletAuthControl.expectAndReturn(
506 servletAuth.isAuthorized( null, session, "internal",
507 ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD ),
510 httpAuthControl.replay();
511 servletAuthControl.replay();
513 WebResponse response = sc.getResponse( request );
515 httpAuthControl.verify();
516 servletAuthControl.verify();
518 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
519 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
522 // test get with valid user with no read access to repo
523 public void testGetWithAValidUserWithNoReadAccess()
526 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
527 String expectedArtifactContents = "dummy-commons-lang-artifact";
529 File artifactFile = new File( repoRootInternal, commonsLangJar );
530 artifactFile.getParentFile().mkdirs();
532 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
534 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
535 InvocationContext ic = sc.newInvocation( request );
536 servlet = (RepositoryServlet) ic.getServlet();
537 servlet.setDavSessionProvider( davSessionProvider );
539 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
540 archivaDavResourceFactory.setHttpAuth( httpAuth );
541 archivaDavResourceFactory.setServletAuth( servletAuth );
543 servlet.setResourceFactory( archivaDavResourceFactory );
545 AuthenticationResult result = new AuthenticationResult();
546 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
547 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
549 // ArchivaDavResourceFactory#isAuthorized()
550 SecuritySession session = new DefaultSecuritySession();
551 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
552 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
553 httpAuthControl.expectAndReturn( httpAuth.getSessionUser( ic.getRequest().getSession() ), new SimpleUser() );
554 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
555 servletAuthControl.expectAndThrow(
556 servletAuth.isAuthorized( null, session, "internal",
557 ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD ),
558 new UnauthorizedException( "User not authorized to read repository." ) );
560 httpAuthControl.replay();
561 servletAuthControl.replay();
563 WebResponse response = sc.getResponse( request );
565 httpAuthControl.verify();
566 servletAuthControl.verify();
568 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );