diff options
author | Maria Odea B. Ching <oching@apache.org> | 2009-10-13 10:36:24 +0000 |
---|---|---|
committer | Maria Odea B. Ching <oching@apache.org> | 2009-10-13 10:36:24 +0000 |
commit | c893eefe9fd685bdcfaf14805d8448665c777125 (patch) | |
tree | ed8b202a595030793893bc402ef362622ff1232a /archiva-modules/archiva-web | |
parent | 88f79fe1e7688750f3236feb71a5288e6891abe1 (diff) | |
download | archiva-c893eefe9fd685bdcfaf14805d8448665c777125.tar.gz archiva-c893eefe9fd685bdcfaf14805d8448665c777125.zip |
[MRM-747] Archiva should prevent re-deployment of released or non-snapshot versioned artifacts
submitted by Marc Lustig
o added checks in webdav to block re-deployment if artifact version already exists in the repo and throw a 409 in such cases
o added tests for deploying and re-deploying an artifact
additional modifications to the patch:
o update checking for artifact types that will be blocked
o add tests for deploying metadata and support file
git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@824677 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'archiva-modules/archiva-web')
5 files changed, 181 insertions, 47 deletions
diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java index 7ca4de562..319104634 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java @@ -40,6 +40,7 @@ import org.apache.jackrabbit.webdav.DavSession; import org.apache.jackrabbit.webdav.lock.LockManager; import org.apache.jackrabbit.webdav.lock.SimpleLockManager; import org.apache.maven.archiva.common.utils.PathUtil; +import org.apache.maven.archiva.common.utils.VersionUtil; import org.apache.maven.archiva.configuration.ArchivaConfiguration; import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration; import org.apache.maven.archiva.model.ArchivaRepositoryMetadata; @@ -162,12 +163,12 @@ public class ArchivaDavResourceFactory * @plexus.requirement role-hint="md5"; */ private Digester digestMd5; - + /** * @plexus.requirement */ private ArchivaTaskScheduler scheduler; - + public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request, final DavServletResponse response ) throws DavException @@ -190,7 +191,7 @@ public class ArchivaDavResourceFactory throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Write method not allowed for repository groups." ); } - + log.debug( "Repository group '" + repoGroupConfig.getId() + "' accessed by '" + activePrincipal + "'" ); // handle browse requests for virtual repos @@ -200,9 +201,16 @@ public class ArchivaDavResourceFactory } else { - resource = - processRepositoryGroup( request, archivaLocator, repoGroupConfig.getRepositories(), - activePrincipal, resourcesInAbsolutePath ); + try + { + resource = + processRepositoryGroup( request, archivaLocator, repoGroupConfig.getRepositories(), + activePrincipal, resourcesInAbsolutePath ); + } + catch ( ReleaseArtifactAlreadyExistsException e ) + { + throw new DavException( HttpServletResponse.SC_CONFLICT ); + } } } else @@ -215,8 +223,8 @@ public class ArchivaDavResourceFactory } catch ( RepositoryNotFoundException e ) { - throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " - + archivaLocator.getRepositoryId() ); + throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " + + archivaLocator.getRepositoryId() ); } catch ( RepositoryException e ) { @@ -225,7 +233,14 @@ public class ArchivaDavResourceFactory log.debug( "Managed repository '" + managedRepository.getId() + "' accessed by '" + activePrincipal + "'" ); - resource = processRepository( request, archivaLocator, activePrincipal, managedRepository ); + try + { + resource = processRepository( request, archivaLocator, activePrincipal, managedRepository ); + } + catch ( ReleaseArtifactAlreadyExistsException e ) + { + throw new DavException( HttpServletResponse.SC_CONFLICT, e ); + } String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ); resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(), logicalResource ).getAbsolutePath() ); @@ -235,8 +250,8 @@ public class ArchivaDavResourceFactory // MRM-872 : merge all available metadata // merge metadata only when requested via the repo group - if ( ( repositoryRequest.isMetadata( requestedResource ) || ( requestedResource.endsWith( "metadata.xml.sha1" ) || requestedResource.endsWith( "metadata.xml.md5" ) ) ) - && repoGroupConfig != null ) + if ( ( repositoryRequest.isMetadata( requestedResource ) || ( requestedResource.endsWith( "metadata.xml.sha1" ) || requestedResource.endsWith( "metadata.xml.md5" ) ) ) && + repoGroupConfig != null ) { // this should only be at the project level not version level! if ( isProjectReference( requestedResource ) ) @@ -334,9 +349,9 @@ public class ArchivaDavResourceFactory private DavResource processRepositoryGroup( final DavServletRequest request, ArchivaDavResourceLocator archivaLocator, List<String> repositories, String activePrincipal, List<String> resourcesInAbsolutePath ) - throws DavException + throws DavException, ReleaseArtifactAlreadyExistsException { - DavResource resource = null; + DavResource resource = null; List<DavException> storedExceptions = new ArrayList<DavException>(); for ( String repositoryId : repositories ) @@ -372,24 +387,24 @@ public class ArchivaDavResourceFactory resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(), logicalResource ).getAbsolutePath() ); } catch ( DavException e ) - { + { storedExceptions.add( e ); } } if ( resource == null ) - { + { if ( !storedExceptions.isEmpty() ) - { + { // MRM-1232 - for( DavException e : storedExceptions ) + for ( DavException e : storedExceptions ) { - if( 401 == e.getErrorCode() ) + if ( 401 == e.getErrorCode() ) { throw e; } } - + throw new DavException( HttpServletResponse.SC_NOT_FOUND ); } else @@ -402,7 +417,7 @@ public class ArchivaDavResourceFactory private DavResource processRepository( final DavServletRequest request, ArchivaDavResourceLocator archivaLocator, String activePrincipal, ManagedRepositoryContent managedRepository ) - throws DavException + throws DavException, ReleaseArtifactAlreadyExistsException { DavResource resource = null; if ( isAuthorized( request, managedRepository.getId() ) ) @@ -412,13 +427,12 @@ public class ArchivaDavResourceFactory { path = path.substring( 1 ); } - LogicalResource logicalResource = new LogicalResource( path ); + LogicalResource logicalResource = new LogicalResource( path ); File resourceFile = new File( managedRepository.getRepoRoot(), path ); resource = - new ArchivaDavResource( resourceFile.getAbsolutePath(), path, - managedRepository.getRepository(), request.getRemoteAddr(), activePrincipal, - request.getDavSession(), archivaLocator, this, mimeTypes, auditListeners, - scheduler ); + new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepository.getRepository(), + request.getRemoteAddr(), activePrincipal, request.getDavSession(), + archivaLocator, this, mimeTypes, auditListeners, scheduler ); if ( WebdavMethodUtil.isReadMethod( request.getMethod() ) ) { @@ -432,7 +446,7 @@ public class ArchivaDavResourceFactory if ( !resource.isCollection() ) { boolean previouslyExisted = resourceFile.exists(); - + // Attempt to fetch the resource from any defined proxy. boolean fromProxy = fetchContentFromProxies( managedRepository, request, logicalResource ); @@ -462,11 +476,11 @@ public class ArchivaDavResourceFactory if ( fromProxy ) { String event = - ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) - + PROXIED_SUFFIX; - + ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) + + PROXIED_SUFFIX; + log.debug( "Proxied artifact '" + resourceFile.getName() + "' in repository '" + - managedRepository.getId() + "' (current user '" + activePrincipal + "')" ); + managedRepository.getId() + "' (current user '" + activePrincipal + "')" ); triggerAuditEvent( request.getRemoteAddr(), archivaLocator.getRepositoryId(), logicalResource.getPath(), event, activePrincipal ); @@ -482,6 +496,35 @@ public class ArchivaDavResourceFactory if ( request.getMethod().equals( HTTP_PUT_METHOD ) ) { + String resourcePath = logicalResource.getPath(); + + // check if target repo is enabled for releases + // we suppose that release-artifacts can deployed only to repos enabled for releases + if ( managedRepository.getRepository().isReleases() && !repositoryRequest.isMetadata( resourcePath ) && + !repositoryRequest.isSupportFile( resourcePath ) ) + { + ArtifactReference artifact = null; + try + { + artifact = managedRepository.toArtifactReference( resourcePath ); + } + catch ( LayoutException e ) + { + throw new DavException( HttpServletResponse.SC_BAD_REQUEST, e ); + } + + if ( !VersionUtil.isSnapshot( artifact.getVersion() ) ) + { + // check if artifact already exists + if ( managedRepository.hasContent( artifact ) ) + { + log.warn( "Overwriting released artifacts is not allowed." ); + throw new ReleaseArtifactAlreadyExistsException( managedRepository.getId(), + "Overwriting released artifacts is not allowed." ); + } + } + } + /* * Create parent directories that don't exist when writing a file This actually makes this * implementation not compliant to the WebDAV RFC - but we have enough knowledge about how the @@ -496,9 +539,10 @@ public class ArchivaDavResourceFactory { destDir.mkdirs(); String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir ); - - log.debug( "Creating destination directory '" + destDir.getName() + "' (current user '" + activePrincipal + "')" ); - + + log.debug( "Creating destination directory '" + destDir.getName() + "' (current user '" + + activePrincipal + "')" ); + triggerAuditEvent( request.getRemoteAddr(), logicalResource.getPath(), relPath, AuditEvent.CREATE_DIR, activePrincipal ); } @@ -519,8 +563,8 @@ public class ArchivaDavResourceFactory } catch ( RepositoryNotFoundException e ) { - throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " - + archivaLocator.getRepositoryId() ); + throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " + + archivaLocator.getRepositoryId() ); } catch ( RepositoryException e ) { @@ -572,8 +616,9 @@ public class ArchivaDavResourceFactory File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact ); resource.setPath( managedRepository.toPath( artifact ) ); - - log.debug( "Proxied artifact '" + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() + "'" ); + + log.debug( "Proxied artifact '" + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + + artifact.getVersion() + "'" ); return ( proxiedFile != null ); } @@ -774,9 +819,9 @@ public class ArchivaDavResourceFactory AuthenticationResult result = httpAuth.getAuthenticationResult( request, null ); SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) ); - return servletAuth.isAuthenticated( request, result ) - && servletAuth.isAuthorized( request, securitySession, repositoryId, - WebdavMethodUtil.getMethodPermission( request.getMethod() ) ); + return servletAuth.isAuthenticated( request, result ) && + servletAuth.isAuthorized( request, securitySession, repositoryId, + WebdavMethodUtil.getMethodPermission( request.getMethod() ) ); } catch ( AuthenticationException e ) { @@ -878,8 +923,8 @@ public class ArchivaDavResourceFactory catch ( DavException e ) { // TODO: review exception handling - log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal - + "': " + e.getMessage() ); + log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + + "': " + e.getMessage() ); } } else @@ -897,8 +942,8 @@ public class ArchivaDavResourceFactory catch ( UnauthorizedException e ) { // TODO: review exception handling - log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal - + "': " + e.getMessage() ); + log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + + "': " + e.getMessage() ); } } } @@ -1057,7 +1102,7 @@ public class ArchivaDavResourceFactory { this.repositoryRequest = repositoryRequest; } - + public void setConnectors( RepositoryProxyConnectors connectors ) { this.connectors = connectors; diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ReleaseArtifactAlreadyExistsException.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ReleaseArtifactAlreadyExistsException.java new file mode 100644 index 000000000..0d46b215e --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ReleaseArtifactAlreadyExistsException.java @@ -0,0 +1,38 @@ +package org.apache.maven.archiva.webdav;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ */
+public class ReleaseArtifactAlreadyExistsException
+ extends Exception
+{
+ final private String repositoryName;
+
+ public ReleaseArtifactAlreadyExistsException( String repositoryName, String message )
+ {
+ this.repositoryName = repositoryName;
+ }
+
+ public String getRepositoryName()
+ {
+ return repositoryName;
+ }
+}
diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/AbstractRepositoryServletTestCase.java b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/AbstractRepositoryServletTestCase.java index 892eeeedb..a0ec2c811 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/AbstractRepositoryServletTestCase.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/AbstractRepositoryServletTestCase.java @@ -111,6 +111,13 @@ public abstract class AbstractRepositoryServletTestCase Assert.assertEquals( "Should have been an 500/Internal Server Error response code.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response .getResponseCode() ); } + + protected void assertResponseConflictError( WebResponse response ) + { + assertNotNull( "Should have received a response", response ); + Assert.assertEquals( "Should have been a 409/Conflict response code.", HttpServletResponse.SC_CONFLICT, + response.getResponseCode() ); + } protected ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location ) { diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletDeployTest.java b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletDeployTest.java index e15c374e7..886fac309 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletDeployTest.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletDeployTest.java @@ -38,20 +38,64 @@ import org.apache.maven.archiva.webdav.httpunit.MkColMethodWebRequest; public class RepositoryServletDeployTest extends AbstractRepositoryServletTestCase { + private static final String ARTIFACT_DEFAULT_LAYOUT = "/path/to/artifact/1.0.0/artifact-1.0.0.jar"; + public void testPutWithMissingParentCollection() throws Exception { setupCleanRepo( repoRootInternal ); - String putUrl = "http://machine.com/repository/internal/path/to/artifact.jar"; + String putUrl = "http://machine.com/repository/internal" + ARTIFACT_DEFAULT_LAYOUT; InputStream is = getClass().getResourceAsStream( "/artifact.jar" ); + // verify that the file exists in resources-dir assertNotNull( "artifact.jar inputstream", is ); WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" ); WebResponse response = sc.getResponse( request ); assertResponseCreated( response ); - assertFileContents( "artifact.jar\n", repoRootInternal, "path/to/artifact.jar" ); + assertFileContents( "artifact.jar\n", repoRootInternal, ARTIFACT_DEFAULT_LAYOUT ); + } + + /** + * MRM-747 + * test whether trying to overwrite existing relase-artifact is blocked by returning HTTP-code 409 + * + * @throws Exception + */ + public void testPreventOverwritingReleaseArtifacts() + throws Exception + { + setupCleanRepo( repoRootInternal ); + + String putUrl = "http://machine.com/repository/internal" + ARTIFACT_DEFAULT_LAYOUT; + String metadataUrl = "http://machine.com/repository/internal/path/to/artifact/maven-metadata.xml"; + String checksumUrl = "http://machine.com/repository/internal" + ARTIFACT_DEFAULT_LAYOUT + ".sha1"; + + InputStream is = getClass().getResourceAsStream( "/artifact.jar" ); + // verify that the file exists in resources-dir + assertNotNull( "artifact.jar inputstream", is ); + + // send request #1 and verify it's successful + WebRequest request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" ); + WebResponse response = sc.getResponse( request ); + assertResponseCreated( response ); + + is = getClass().getResourceAsStream( "/artifact.jar.sha1" ); + request = new PutMethodWebRequest( checksumUrl, is, "application/octet-stream" ); + response = sc.getResponse( request ); + assertResponseCreated( response ); + + is = getClass().getResourceAsStream( "/maven-metadata.xml" ); + request = new PutMethodWebRequest( metadataUrl, is, "application/octet-stream" ); + response = sc.getResponse( request ); + assertResponseCreated( response ); + + // send request #2 and verify it's blocked + is = getClass().getResourceAsStream( "/artifact.jar" ); + request = new PutMethodWebRequest( putUrl, is, "application/octet-stream" ); + response = sc.getResponse( request ); + assertResponseConflictError( response ); } public void testMkColWithMissingParentCollectionFails() diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/resources/artifact.jar.sha1 b/archiva-modules/archiva-web/archiva-webdav/src/test/resources/artifact.jar.sha1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/resources/artifact.jar.sha1 |