1 package org.apache.archiva.web.servlet;
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.maven.archiva.configuration.ArchivaConfiguration;
32 import org.apache.maven.archiva.configuration.Configuration;
33 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
34 import org.apache.maven.archiva.security.ArchivaXworkUser;
35 import org.apache.maven.archiva.security.ServletAuthenticator;
36 import org.codehaus.plexus.redback.authentication.AuthenticationException;
37 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
38 import org.codehaus.plexus.redback.authorization.UnauthorizedException;
39 import org.codehaus.plexus.redback.system.DefaultSecuritySession;
40 import org.codehaus.plexus.redback.system.SecuritySession;
41 import org.codehaus.plexus.spring.PlexusInSpringTestCase;
42 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
43 import org.codehaus.redback.integration.filter.authentication.basic.HttpBasicAuthentication;
44 import org.easymock.MockControl;
45 import org.easymock.classextension.MockClassControl;
47 import com.meterware.httpunit.GetMethodWebRequest;
48 import com.meterware.httpunit.HttpUnitOptions;
49 import com.meterware.httpunit.PutMethodWebRequest;
50 import com.meterware.httpunit.WebRequest;
51 import com.meterware.httpunit.WebResponse;
52 import com.meterware.servletunit.InvocationContext;
53 import com.meterware.servletunit.ServletRunner;
54 import com.meterware.servletunit.ServletUnitClient;
57 * RepositoryServletSecurityTest
59 * Test the flow of the authentication and authorization checks. This does not necessarily
60 * perform redback security checking.
64 public class RepositoryServletSecurityTest
65 extends PlexusInSpringTestCase
67 protected static final String REPOID_INTERNAL = "internal";
69 protected ServletUnitClient sc;
71 protected File repoRootInternal;
73 private ServletRunner sr;
75 protected ArchivaConfiguration archivaConfiguration;
77 private DavSessionProvider davSessionProvider;
79 private MockControl servletAuthControl;
81 private ServletAuthenticator servletAuth;
83 private MockClassControl httpAuthControl;
85 private HttpAuthenticator httpAuth;
87 private ArchivaXworkUser archivaXworkUser;
89 private RepositoryServlet servlet;
96 String appserverBase = getTestFile( "target/appserver-base" ).getAbsolutePath();
97 System.setProperty( "appserver.base", appserverBase );
99 File testConf = getTestFile( "src/test/resources/repository-archiva.xml" );
100 File testConfDest = new File( appserverBase, "conf/archiva.xml" );
101 FileUtils.copyFile( testConf, testConfDest );
103 archivaConfiguration = (ArchivaConfiguration) lookup( ArchivaConfiguration.class );
104 repoRootInternal = new File( appserverBase, "data/repositories/internal" );
105 Configuration config = archivaConfiguration.getConfiguration();
107 config.addManagedRepository( createManagedRepository( REPOID_INTERNAL, "Internal Test Repo", repoRootInternal ) );
108 saveConfiguration( archivaConfiguration );
110 CacheManager.getInstance().removeCache( "url-failures-cache" );
112 HttpUnitOptions.setExceptionsThrownOnErrorStatus( false );
114 sr = new ServletRunner( getTestFile( "src/test/resources/WEB-INF/repository-servlet-security-test/web.xml" ) );
115 sr.registerServlet( "/repository/*", RepositoryServlet.class.getName() );
118 servletAuthControl = MockControl.createControl( ServletAuthenticator.class );
119 servletAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
120 servletAuth = (ServletAuthenticator) servletAuthControl.getMock();
123 MockClassControl.createControl( HttpBasicAuthentication.class, HttpBasicAuthentication.class.getMethods() );
124 httpAuthControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
125 httpAuth = (HttpAuthenticator) httpAuthControl.getMock();
127 archivaXworkUser = new ArchivaXworkUser();
128 archivaXworkUser.setGuest( "guest" );
130 davSessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth, archivaXworkUser );
133 protected ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location )
135 ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();
137 repo.setName( name );
138 repo.setLocation( location.getAbsolutePath() );
142 protected void saveConfiguration()
145 saveConfiguration( archivaConfiguration );
148 protected void saveConfiguration( ArchivaConfiguration archivaConfiguration )
151 archivaConfiguration.save( archivaConfiguration.getConfiguration() );
154 protected void setupCleanRepo( File repoRootDir )
157 FileUtils.deleteDirectory( repoRootDir );
158 if ( !repoRootDir.exists() )
160 repoRootDir.mkdirs();
165 protected String getPlexusConfigLocation()
167 return "org/apache/maven/archiva/webdav/RepositoryServletSecurityTest.xml";
171 protected void tearDown()
184 if ( repoRootInternal.exists() )
186 FileUtils.deleteDirectory(repoRootInternal);
194 // test deploy with invalid user, and guest has no write access to repo
195 // 401 must be returned
196 public void testPutWithInvalidUserAndGuestHasNoWriteAccess()
199 setupCleanRepo( repoRootInternal );
201 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
202 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
203 assertNotNull( "artifact.jar inputstream", is );
205 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
206 InvocationContext ic = sc.newInvocation( request );
207 servlet = (RepositoryServlet) ic.getServlet();
208 servlet.setDavSessionProvider( davSessionProvider );
210 AuthenticationResult result = new AuthenticationResult();
211 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
212 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
213 new AuthenticationException( "Authentication error" ) );
215 servletAuth.isAuthorized( "guest", "internal", true );
216 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
217 servletAuthControl.setThrowable( new UnauthorizedException( "'guest' has no write access to repository" ) );
219 httpAuthControl.replay();
220 servletAuthControl.replay();
222 servlet.service( ic.getRequest(), ic.getResponse() );
224 httpAuthControl.verify();
225 servletAuthControl.verify();
227 //assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
230 // test deploy with invalid user, but guest has write access to repo
231 public void testPutWithInvalidUserAndGuestHasWriteAccess()
234 setupCleanRepo( repoRootInternal );
236 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
237 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
238 assertNotNull( "artifact.jar inputstream", is );
240 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
242 InvocationContext ic = sc.newInvocation( request );
243 servlet = (RepositoryServlet) ic.getServlet();
244 servlet.setDavSessionProvider( davSessionProvider );
246 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
247 archivaDavResourceFactory.setHttpAuth( httpAuth );
248 archivaDavResourceFactory.setServletAuth( servletAuth );
250 servlet.setResourceFactory( archivaDavResourceFactory );
252 AuthenticationResult result = new AuthenticationResult();
253 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
254 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
255 new AuthenticationException( "Authentication error" ) );
257 servletAuth.isAuthorized( "guest", "internal", true );
258 servletAuthControl.setMatcher( MockControl.EQUALS_MATCHER );
259 servletAuthControl.setReturnValue( true );
261 // ArchivaDavResourceFactory#isAuthorized()
262 SecuritySession session = new DefaultSecuritySession();
263 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
264 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true) ), session );
265 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, result ),
266 new AuthenticationException( "Authentication error" ) );
268 // check if guest has write access
269 servletAuth.isAuthorized( "guest", "internal", true );
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 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
314 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
315 new UnauthorizedException( "User not authorized" ) );
317 httpAuthControl.replay();
318 servletAuthControl.replay();
320 servlet.service( ic.getRequest(), ic.getResponse() );
322 httpAuthControl.verify();
323 servletAuthControl.verify();
325 // assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode());
328 // test deploy with a valid user with write access
329 public void testPutWithValidUserWithWriteAccess()
332 setupCleanRepo( repoRootInternal );
333 assertTrue( repoRootInternal.exists() );
335 String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar";
336 InputStream is = getClass().getResourceAsStream( "/artifact.jar" );
337 assertNotNull( "artifact.jar inputstream", is );
339 WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" );
341 InvocationContext ic = sc.newInvocation( request );
342 servlet = (RepositoryServlet) ic.getServlet();
343 servlet.setDavSessionProvider( davSessionProvider );
345 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
346 archivaDavResourceFactory.setHttpAuth( httpAuth );
347 archivaDavResourceFactory.setServletAuth( servletAuth );
349 servlet.setResourceFactory( archivaDavResourceFactory );
351 AuthenticationResult result = new AuthenticationResult();
352 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
353 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
355 // ArchivaDavResourceFactory#isAuthorized()
356 SecuritySession session = new DefaultSecuritySession();
357 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
358 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
359 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
360 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
362 httpAuthControl.replay();
363 servletAuthControl.replay();
365 servlet.service( ic.getRequest(), ic.getResponse() );
367 httpAuthControl.verify();
368 servletAuthControl.verify();
370 // assertEquals(HttpServletResponse.SC_CREATED, response.getResponseCode());
373 // test get with invalid user, and guest has read access to repo
374 public void testGetWithInvalidUserAndGuestHasReadAccess()
377 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
378 String expectedArtifactContents = "dummy-commons-lang-artifact";
380 File artifactFile = new File( repoRootInternal, commonsLangJar );
381 artifactFile.getParentFile().mkdirs();
383 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
385 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
386 InvocationContext ic = sc.newInvocation( request );
387 servlet = (RepositoryServlet) ic.getServlet();
388 servlet.setDavSessionProvider( davSessionProvider );
390 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
391 archivaDavResourceFactory.setHttpAuth( httpAuth );
392 archivaDavResourceFactory.setServletAuth( servletAuth );
394 servlet.setResourceFactory( archivaDavResourceFactory );
396 AuthenticationResult result = new AuthenticationResult();
397 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
398 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
399 new AuthenticationException( "Authentication error" ) );
400 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), true );
402 // ArchivaDavResourceFactory#isAuthorized()
403 SecuritySession session = new DefaultSecuritySession();
404 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
405 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
406 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
407 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
409 httpAuthControl.replay();
410 servletAuthControl.replay();
412 WebResponse response = sc.getResponse( request );
414 httpAuthControl.verify();
415 servletAuthControl.verify();
417 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
418 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
421 // test get with invalid user, and guest has no read access to repo
422 public void testGetWithInvalidUserAndGuestHasNoReadAccess()
425 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
426 String expectedArtifactContents = "dummy-commons-lang-artifact";
428 File artifactFile = new File( repoRootInternal, commonsLangJar );
429 artifactFile.getParentFile().mkdirs();
431 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
433 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
434 InvocationContext ic = sc.newInvocation( request );
435 servlet = (RepositoryServlet) ic.getServlet();
436 servlet.setDavSessionProvider( davSessionProvider );
438 AuthenticationResult result = new AuthenticationResult();
439 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
440 servletAuthControl.expectAndThrow( servletAuth.isAuthenticated( null, null ),
441 new AuthenticationException( "Authentication error" ) );
442 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( "guest", "internal", false ), false );
444 httpAuthControl.replay();
445 servletAuthControl.replay();
447 WebResponse response = sc.getResponse( request );
449 httpAuthControl.verify();
450 servletAuthControl.verify();
452 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );
455 // test get with valid user with read access to repo
456 public void testGetWithAValidUserWithReadAccess()
459 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
460 String expectedArtifactContents = "dummy-commons-lang-artifact";
462 File artifactFile = new File( repoRootInternal, commonsLangJar );
463 artifactFile.getParentFile().mkdirs();
465 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
467 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
468 InvocationContext ic = sc.newInvocation( request );
469 servlet = (RepositoryServlet) ic.getServlet();
470 servlet.setDavSessionProvider( davSessionProvider );
472 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
473 archivaDavResourceFactory.setHttpAuth( httpAuth );
474 archivaDavResourceFactory.setServletAuth( servletAuth );
476 servlet.setResourceFactory( archivaDavResourceFactory );
478 AuthenticationResult result = new AuthenticationResult();
479 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
480 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
482 // ArchivaDavResourceFactory#isAuthorized()
483 SecuritySession session = new DefaultSecuritySession();
484 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
485 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
486 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
487 servletAuthControl.expectAndReturn( servletAuth.isAuthorized( null, session, "internal", true ), true );
489 httpAuthControl.replay();
490 servletAuthControl.replay();
492 WebResponse response = sc.getResponse( request );
494 httpAuthControl.verify();
495 servletAuthControl.verify();
497 assertEquals( HttpServletResponse.SC_OK, response.getResponseCode() );
498 assertEquals( "Expected file contents", expectedArtifactContents, response.getText() );
501 // test get with valid user with no read access to repo
502 public void testGetWithAValidUserWithNoReadAccess()
505 String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar";
506 String expectedArtifactContents = "dummy-commons-lang-artifact";
508 File artifactFile = new File( repoRootInternal, commonsLangJar );
509 artifactFile.getParentFile().mkdirs();
511 FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null );
513 WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar );
514 InvocationContext ic = sc.newInvocation( request );
515 servlet = (RepositoryServlet) ic.getServlet();
516 servlet.setDavSessionProvider( davSessionProvider );
518 ArchivaDavResourceFactory archivaDavResourceFactory = (ArchivaDavResourceFactory) servlet.getResourceFactory();
519 archivaDavResourceFactory.setHttpAuth( httpAuth );
520 archivaDavResourceFactory.setServletAuth( servletAuth );
522 servlet.setResourceFactory( archivaDavResourceFactory );
524 AuthenticationResult result = new AuthenticationResult();
525 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
526 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, null ), true );
528 // ArchivaDavResourceFactory#isAuthorized()
529 SecuritySession session = new DefaultSecuritySession();
530 httpAuthControl.expectAndReturn( httpAuth.getAuthenticationResult( null, null ), result );
531 httpAuthControl.expectAndReturn( httpAuth.getSecuritySession( ic.getRequest().getSession( true ) ), session );
532 servletAuthControl.expectAndReturn( servletAuth.isAuthenticated( null, result ), true );
533 servletAuthControl.expectAndThrow( servletAuth.isAuthorized( null, session, "internal", true ),
534 new UnauthorizedException( "User not authorized to read repository." ) );
536 httpAuthControl.replay();
537 servletAuthControl.replay();
539 WebResponse response = sc.getResponse( request );
541 httpAuthControl.verify();
542 servletAuthControl.verify();
544 assertEquals( HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() );