[MRM-1302] add some prevention for concurrent modification of a list

git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@917398 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brett Porter 2010-03-01 05:56:12 +00:00
parent 4cb99823a0
commit 31891b80e4
1 changed files with 93 additions and 83 deletions

View File

@ -19,14 +19,6 @@ package org.apache.maven.archiva.webdav;
* under the License. * under the License.
*/ */
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler; import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -86,6 +78,14 @@ import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
/** /**
* @plexus.component role="org.apache.maven.archiva.webdav.ArchivaDavResourceFactory" * @plexus.component role="org.apache.maven.archiva.webdav.ArchivaDavResourceFactory"
*/ */
@ -200,9 +200,12 @@ public class ArchivaDavResourceFactory
} }
else else
{ {
resource = // make a copy to avoid potential concurrent modifications (eg. by configuration)
processRepositoryGroup( request, archivaLocator, repoGroupConfig.getRepositories(), // TODO: ultimately, locking might be more efficient than copying in this fashion since updates are
activePrincipal, resourcesInAbsolutePath ); // infrequent
ArrayList<String> repositories = new ArrayList<String>( repoGroupConfig.getRepositories() );
resource = processRepositoryGroup( request, archivaLocator, repositories, activePrincipal,
resourcesInAbsolutePath );
} }
} }
else else
@ -215,8 +218,8 @@ public class ArchivaDavResourceFactory
} }
catch ( RepositoryNotFoundException e ) catch ( RepositoryNotFoundException e )
{ {
throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " + throw new DavException( HttpServletResponse.SC_NOT_FOUND,
archivaLocator.getRepositoryId() ); "Invalid repository: " + archivaLocator.getRepositoryId() );
} }
catch ( RepositoryException e ) catch ( RepositoryException e )
{ {
@ -228,15 +231,16 @@ public class ArchivaDavResourceFactory
resource = processRepository( request, archivaLocator, activePrincipal, managedRepository ); resource = processRepository( request, archivaLocator, activePrincipal, managedRepository );
String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ); String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() );
resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(), logicalResource ).getAbsolutePath() ); resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(),
logicalResource ).getAbsolutePath() );
} }
String requestedResource = request.getRequestURI(); String requestedResource = request.getRequestURI();
// MRM-872 : merge all available metadata // MRM-872 : merge all available metadata
// merge metadata only when requested via the repo group // merge metadata only when requested via the repo group
if ( ( repositoryRequest.isMetadata( requestedResource ) || repositoryRequest.isMetadataSupportFile( requestedResource ) ) && if ( ( repositoryRequest.isMetadata( requestedResource ) || repositoryRequest.isMetadataSupportFile(
repoGroupConfig != null ) requestedResource ) ) && repoGroupConfig != null )
{ {
// this should only be at the project level not version level! // this should only be at the project level not version level!
if ( isProjectReference( requestedResource ) ) if ( isProjectReference( requestedResource ) )
@ -245,25 +249,24 @@ public class ArchivaDavResourceFactory
artifactId = StringUtils.substringAfterLast( artifactId, "/" ); artifactId = StringUtils.substringAfterLast( artifactId, "/" );
ArchivaDavResource res = (ArchivaDavResource) resource; ArchivaDavResource res = (ArchivaDavResource) resource;
String filePath = String filePath = StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace(
StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ), "/" ); '\\', '/' ), "/" );
filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml"; filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml";
// for MRM-872 handle checksums of the merged metadata files // for MRM-872 handle checksums of the merged metadata files
if ( repositoryRequest.isSupportFile( requestedResource ) ) if ( repositoryRequest.isSupportFile( requestedResource ) )
{ {
File metadataChecksum = File metadataChecksum = new File( filePath + "." + StringUtils.substringAfterLast(
new File( filePath + "." + StringUtils.substringAfterLast( requestedResource, "." ) ); requestedResource, "." ) );
if ( metadataChecksum.exists() ) if ( metadataChecksum.exists() )
{ {
LogicalResource logicalResource = LogicalResource logicalResource = new LogicalResource( RepositoryPathUtil.getLogicalResource(
new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); locator.getResourcePath() ) );
resource = resource = new ArchivaDavResource( metadataChecksum.getAbsolutePath(),
new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), logicalResource.getPath(), null, request.getRemoteAddr(),
null, request.getRemoteAddr(), activePrincipal, activePrincipal, request.getDavSession(), archivaLocator,
request.getDavSession(), archivaLocator, this, mimeTypes, this, mimeTypes, auditListeners, scheduler );
auditListeners, scheduler );
} }
} }
else else
@ -291,14 +294,13 @@ public class ArchivaDavResourceFactory
{ {
File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath ); File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath );
LogicalResource logicalResource = LogicalResource logicalResource = new LogicalResource(
new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
resource = resource = new ArchivaDavResource( resourceFile.getAbsolutePath(),
new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), logicalResource.getPath(), null, request.getRemoteAddr(),
null, request.getRemoteAddr(), activePrincipal, activePrincipal, request.getDavSession(), archivaLocator,
request.getDavSession(), archivaLocator, this, mimeTypes, this, mimeTypes, auditListeners, scheduler );
auditListeners, scheduler );
} }
catch ( RepositoryMetadataException r ) catch ( RepositoryMetadataException r )
{ {
@ -341,7 +343,7 @@ public class ArchivaDavResourceFactory
for ( String repositoryId : repositories ) for ( String repositoryId : repositories )
{ {
ManagedRepositoryContent managedRepository = null; ManagedRepositoryContent managedRepository;
try try
{ {
managedRepository = repositoryFactory.getManagedRepositoryContent( repositoryId ); managedRepository = repositoryFactory.getManagedRepositoryContent( repositoryId );
@ -357,8 +359,8 @@ public class ArchivaDavResourceFactory
try try
{ {
DavResource updatedResource = DavResource updatedResource = processRepository( request, archivaLocator, activePrincipal,
processRepository( request, archivaLocator, activePrincipal, managedRepository ); managedRepository );
if ( resource == null ) if ( resource == null )
{ {
resource = updatedResource; resource = updatedResource;
@ -369,7 +371,8 @@ public class ArchivaDavResourceFactory
{ {
logicalResource = logicalResource.substring( 1 ); logicalResource = logicalResource.substring( 1 );
} }
resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(), logicalResource ).getAbsolutePath() ); resourcesInAbsolutePath.add( new File( managedRepository.getRepoRoot(),
logicalResource ).getAbsolutePath() );
} }
catch ( DavException e ) catch ( DavException e )
{ {
@ -414,8 +417,7 @@ public class ArchivaDavResourceFactory
} }
LogicalResource logicalResource = new LogicalResource( path ); LogicalResource logicalResource = new LogicalResource( path );
File resourceFile = new File( managedRepository.getRepoRoot(), path ); File resourceFile = new File( managedRepository.getRepoRoot(), path );
resource = resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepository.getRepository(),
new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepository.getRepository(),
request.getRemoteAddr(), activePrincipal, request.getDavSession(), request.getRemoteAddr(), activePrincipal, request.getDavSession(),
archivaLocator, this, mimeTypes, auditListeners, scheduler ); archivaLocator, this, mimeTypes, auditListeners, scheduler );
@ -441,14 +443,15 @@ public class ArchivaDavResourceFactory
{ {
// Perform an adjustment of the resource to the managed // Perform an adjustment of the resource to the managed
// repository expected path. // repository expected path.
String localResourcePath = String localResourcePath = repositoryRequest.toNativePath( logicalResource.getPath(),
repositoryRequest.toNativePath( logicalResource.getPath(), managedRepository ); managedRepository );
resourceFile = new File( managedRepository.getRepoRoot(), localResourcePath ); resourceFile = new File( managedRepository.getRepoRoot(), localResourcePath );
resource = resource = new ArchivaDavResource( resourceFile.getAbsolutePath(),
new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), logicalResource.getPath(),
managedRepository.getRepository(), request.getRemoteAddr(), managedRepository.getRepository(),
activePrincipal, request.getDavSession(), archivaLocator, this, request.getRemoteAddr(), activePrincipal,
mimeTypes, auditListeners, scheduler ); request.getDavSession(), archivaLocator, this, mimeTypes,
auditListeners, scheduler );
} }
catch ( LayoutException e ) catch ( LayoutException e )
{ {
@ -460,8 +463,7 @@ public class ArchivaDavResourceFactory
if ( fromProxy ) if ( fromProxy )
{ {
String event = String event = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) +
( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) +
PROXIED_SUFFIX; PROXIED_SUFFIX;
log.debug( "Proxied artifact '" + resourceFile.getName() + "' in repository '" + log.debug( "Proxied artifact '" + resourceFile.getName() + "' in repository '" +
@ -496,9 +498,11 @@ public class ArchivaDavResourceFactory
if ( !VersionUtil.isSnapshot( artifact.getVersion() ) ) if ( !VersionUtil.isSnapshot( artifact.getVersion() ) )
{ {
// check if artifact already exists and if artifact re-deployment to the repository is allowed // check if artifact already exists and if artifact re-deployment to the repository is allowed
if ( managedRepository.hasContent( artifact ) && managedRepository.getRepository().isBlockRedeployments() ) if ( managedRepository.hasContent( artifact ) &&
managedRepository.getRepository().isBlockRedeployments() )
{ {
log.warn( "Overwriting released artifacts in repository '" + managedRepository.getId() + "' is not allowed." ); log.warn( "Overwriting released artifacts in repository '" + managedRepository.getId() +
"' is not allowed." );
throw new DavException( HttpServletResponse.SC_CONFLICT, throw new DavException( HttpServletResponse.SC_CONFLICT,
"Overwriting released artifacts is not allowed." ); "Overwriting released artifacts is not allowed." );
} }
@ -525,8 +529,9 @@ public class ArchivaDavResourceFactory
destDir.mkdirs(); destDir.mkdirs();
String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir ); String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
log.debug( "Creating destination directory '" + destDir.getName() + "' (current user '" + log.debug(
activePrincipal + "')" ); "Creating destination directory '" + destDir.getName() + "' (current user '" + activePrincipal +
"')" );
triggerAuditEvent( request.getRemoteAddr(), managedRepository.getId(), relPath, triggerAuditEvent( request.getRemoteAddr(), managedRepository.getId(), relPath,
AuditEvent.CREATE_DIR, activePrincipal ); AuditEvent.CREATE_DIR, activePrincipal );
@ -548,8 +553,8 @@ public class ArchivaDavResourceFactory
} }
catch ( RepositoryNotFoundException e ) catch ( RepositoryNotFoundException e )
{ {
throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid repository: " + throw new DavException( HttpServletResponse.SC_NOT_FOUND,
archivaLocator.getRepositoryId() ); "Invalid repository: " + archivaLocator.getRepositoryId() );
} }
catch ( RepositoryException e ) catch ( RepositoryException e )
{ {
@ -562,9 +567,9 @@ public class ArchivaDavResourceFactory
logicalResource = logicalResource.substring( 1 ); logicalResource = logicalResource.substring( 1 );
} }
File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource ); File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource );
DavResource resource = DavResource resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource,
new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource, managedRepository.getRepository(), managedRepository.getRepository(), davSession, archivaLocator,
davSession, archivaLocator, this, mimeTypes, auditListeners, scheduler ); this, mimeTypes, auditListeners, scheduler );
resource.addLockManager( lockManager ); resource.addLockManager( lockManager );
return resource; return resource;
@ -615,7 +620,8 @@ public class ArchivaDavResourceFactory
catch ( ProxyDownloadException e ) catch ( ProxyDownloadException e )
{ {
log.error( e.getMessage(), e ); log.error( e.getMessage(), e );
throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to fetch artifact resource." ); throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Unable to fetch artifact resource." );
} }
return false; return false;
} }
@ -708,6 +714,7 @@ public class ArchivaDavResourceFactory
} }
// TODO: remove? // TODO: remove?
private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action, private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action,
String principal ) String principal )
{ {
@ -804,9 +811,10 @@ public class ArchivaDavResourceFactory
AuthenticationResult result = httpAuth.getAuthenticationResult( request, null ); AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) ); SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) );
return servletAuth.isAuthenticated( request, result ) && return servletAuth.isAuthenticated( request, result ) && servletAuth.isAuthorized( request, securitySession,
servletAuth.isAuthorized( request, securitySession, repositoryId, repositoryId,
WebdavMethodUtil.getMethodPermission( request.getMethod() ) ); WebdavMethodUtil.getMethodPermission(
request.getMethod() ) );
} }
catch ( AuthenticationException e ) catch ( AuthenticationException e )
{ {
@ -814,8 +822,7 @@ public class ArchivaDavResourceFactory
String guest = UserManager.GUEST_USERNAME; String guest = UserManager.GUEST_USERNAME;
try try
{ {
if ( servletAuth.isAuthorized( if ( servletAuth.isAuthorized( guest,
guest,
( (ArchivaDavResourceLocator) request.getRequestLocator() ).getRepositoryId(), ( (ArchivaDavResourceLocator) request.getRequestLocator() ).getRepositoryId(),
WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) ) WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
{ {
@ -908,8 +915,9 @@ public class ArchivaDavResourceFactory
catch ( DavException e ) catch ( DavException e )
{ {
// TODO: review exception handling // TODO: review exception handling
log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + log.debug(
"': " + e.getMessage() ); "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + "': " +
e.getMessage() );
} }
} }
else else
@ -918,7 +926,8 @@ public class ArchivaDavResourceFactory
try try
{ {
if ( servletAuth.isAuthorized( activePrincipal, repository, if ( servletAuth.isAuthorized( activePrincipal, repository,
WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) ) WebdavMethodUtil.getMethodPermission(
request.getMethod() ) ) )
{ {
mergedRepositoryContents.add( resourceFile ); mergedRepositoryContents.add( resourceFile );
log.debug( "Repository '" + repository + "' accessed by '" + activePrincipal + "'" ); log.debug( "Repository '" + repository + "' accessed by '" + activePrincipal + "'" );
@ -927,8 +936,9 @@ public class ArchivaDavResourceFactory
catch ( UnauthorizedException e ) catch ( UnauthorizedException e )
{ {
// TODO: review exception handling // TODO: review exception handling
log.debug( "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + log.debug(
"': " + e.getMessage() ); "Skipping repository '" + managedRepository + "' for user '" + activePrincipal + "': " +
e.getMessage() );
} }
} }
} }
@ -939,9 +949,9 @@ public class ArchivaDavResourceFactory
throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." ); throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
} }
ArchivaVirtualDavResource resource = ArchivaVirtualDavResource resource = new ArchivaVirtualDavResource( mergedRepositoryContents,
new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, logicalResource.getPath(), mimeTypes,
this ); locator, this );
// compatibility with MRM-440 to ensure browsing the repository group works ok // compatibility with MRM-440 to ensure browsing the repository group works ok
if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) ) if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) )
@ -995,8 +1005,8 @@ public class ArchivaDavResourceFactory
{ {
try try
{ {
if ( servletAuth.isAuthorized( activePrincipal, repository, if ( servletAuth.isAuthorized( activePrincipal, repository, WebdavMethodUtil.getMethodPermission(
WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) ) request.getMethod() ) ) )
{ {
allow = true; allow = true;
break; break;