diff options
author | Martin Stockhammer <martin_s@apache.org> | 2021-05-30 19:14:03 +0200 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2021-05-30 19:14:03 +0200 |
commit | 79c61a7c6a63be33a2221a0bd75fdfea7f6b1bf6 (patch) | |
tree | 5a12b8ad5d8755f5d0f0b6c189bd6a69ceee2bc3 /archiva-modules/archiva-web/archiva-rest/archiva-rest-services | |
parent | fe117fcc4be288a37db07788a2fd3cc857beeb28 (diff) | |
download | archiva-79c61a7c6a63be33a2221a0bd75fdfea7f6b1bf6.tar.gz archiva-79c61a7c6a63be33a2221a0bd75fdfea7f6b1bf6.zip |
Improving storage handling with assets
Diffstat (limited to 'archiva-modules/archiva-web/archiva-rest/archiva-rest-services')
-rw-r--r-- | archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java | 276 |
1 files changed, 249 insertions, 27 deletions
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java index 7fbfe16ce..6fd85d5cf 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultMavenManagedRepositoryService.java @@ -17,27 +17,62 @@ package org.apache.archiva.rest.services.v2; * under the License. */ +import org.apache.archiva.admin.model.AuditInformation; import org.apache.archiva.admin.model.RepositoryAdminException; import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; import org.apache.archiva.components.rest.model.PagedResult; import org.apache.archiva.components.rest.util.QueryHelper; +import org.apache.archiva.redback.authentication.AuthenticationResult; +import org.apache.archiva.redback.authorization.AuthorizationException; +import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal; +import org.apache.archiva.redback.rest.services.RedbackRequestInformation; +import org.apache.archiva.redback.system.DefaultSecuritySession; +import org.apache.archiva.redback.system.SecuritySession; +import org.apache.archiva.redback.system.SecuritySystem; +import org.apache.archiva.redback.users.User; +import org.apache.archiva.redback.users.UserManagerException; +import org.apache.archiva.redback.users.UserNotFoundException; import org.apache.archiva.repository.ManagedRepository; +import org.apache.archiva.repository.ReleaseScheme; +import org.apache.archiva.repository.Repository; +import org.apache.archiva.repository.RepositoryException; import org.apache.archiva.repository.RepositoryRegistry; +import org.apache.archiva.repository.RepositoryType; +import org.apache.archiva.repository.content.ContentItem; +import org.apache.archiva.repository.content.LayoutException; +import org.apache.archiva.repository.storage.fs.FilesystemStorage; +import org.apache.archiva.repository.storage.fs.FsStorageUtil; +import org.apache.archiva.repository.storage.util.StorageUtil; import org.apache.archiva.rest.api.model.v2.Artifact; import org.apache.archiva.rest.api.model.v2.FileInfo; import org.apache.archiva.rest.api.model.v2.MavenManagedRepository; +import org.apache.archiva.rest.api.model.v2.MavenManagedRepositoryUpdate; import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException; import org.apache.archiva.rest.api.services.v2.ErrorKeys; import org.apache.archiva.rest.api.services.v2.ErrorMessage; import org.apache.archiva.rest.api.services.v2.MavenManagedRepositoryService; +import org.apache.archiva.security.common.ArchivaRoleConstants; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.inject.Inject; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS; +import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD; /** * @author Martin Stockhammer <martin_s@apache.org> @@ -45,81 +80,196 @@ import java.util.List; @Service("v2.managedMavenRepositoryService#rest") public class DefaultMavenManagedRepositoryService implements MavenManagedRepositoryService { + @Context + HttpServletResponse httpServletResponse; + + @Context + UriInfo uriInfo; + private static final Logger log = LoggerFactory.getLogger( DefaultMavenManagedRepositoryService.class ); - private static final QueryHelper<org.apache.archiva.admin.model.beans.ManagedRepository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} ); + private static final QueryHelper<ManagedRepository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} ); static { - QUERY_HELPER.addStringFilter( "id", org.apache.archiva.admin.model.beans.ManagedRepository::getId ); - QUERY_HELPER.addStringFilter( "name", org.apache.archiva.admin.model.beans.ManagedRepository::getName ); - QUERY_HELPER.addStringFilter( "location", org.apache.archiva.admin.model.beans.ManagedRepository::getName ); - QUERY_HELPER.addBooleanFilter( "snapshot", org.apache.archiva.admin.model.beans.ManagedRepository::isSnapshots ); - QUERY_HELPER.addBooleanFilter( "release", org.apache.archiva.admin.model.beans.ManagedRepository::isReleases ); - QUERY_HELPER.addNullsafeFieldComparator( "id", org.apache.archiva.admin.model.beans.ManagedRepository::getId ); - QUERY_HELPER.addNullsafeFieldComparator( "name", org.apache.archiva.admin.model.beans.ManagedRepository::getName ); + QUERY_HELPER.addStringFilter( "id", ManagedRepository::getId ); + QUERY_HELPER.addStringFilter( "name", ManagedRepository::getName ); + QUERY_HELPER.addStringFilter( "location", (r) -> r.getLocation().toString() ); + QUERY_HELPER.addBooleanFilter( "snapshot", (r) -> r.getActiveReleaseSchemes( ).contains( ReleaseScheme.SNAPSHOT ) ); + QUERY_HELPER.addBooleanFilter( "release", (r) -> r.getActiveReleaseSchemes().contains( ReleaseScheme.RELEASE )); + QUERY_HELPER.addNullsafeFieldComparator( "id", ManagedRepository::getId ); + QUERY_HELPER.addNullsafeFieldComparator( "name", ManagedRepository::getName ); } private ManagedRepositoryAdmin managedRepositoryAdmin; private RepositoryRegistry repositoryRegistry; + private SecuritySystem securitySystem; - public DefaultMavenManagedRepositoryService( RepositoryRegistry repositoryRegistry, ManagedRepositoryAdmin managedRepositoryAdmin ) + public DefaultMavenManagedRepositoryService( SecuritySystem securitySystem, RepositoryRegistry repositoryRegistry, ManagedRepositoryAdmin managedRepositoryAdmin ) { + this.securitySystem = securitySystem; + this.repositoryRegistry = repositoryRegistry; this.managedRepositoryAdmin = managedRepositoryAdmin; } + protected AuditInformation getAuditInformation( ) + { + RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get( ); + User user = redbackRequestInformation == null ? null : redbackRequestInformation.getUser( ); + String remoteAddr = redbackRequestInformation == null ? null : redbackRequestInformation.getRemoteAddr( ); + return new AuditInformation( user, remoteAddr ); + } + @Override - public PagedResult<MavenManagedRepository> getManagedRepositories( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws ArchivaRestServiceException + public PagedResult<MavenManagedRepository> getManagedRepositories( final String searchTerm, final Integer offset, + final Integer limit, final List<String> orderBy, + final String order ) throws ArchivaRestServiceException { try { - List<org.apache.archiva.admin.model.beans.ManagedRepository> result = managedRepositoryAdmin.getManagedRepositories( ); - int totalCount = Math.toIntExact( result.stream( ).count( ) ); + Collection<ManagedRepository> repos = repositoryRegistry.getManagedRepositories( ); + final Predicate<ManagedRepository> queryFilter = QUERY_HELPER.getQueryFilter( searchTerm ).and( r -> r.getType() == RepositoryType.MAVEN ); + final Comparator<ManagedRepository> comparator = QUERY_HELPER.getComparator( orderBy, order ); + int totalCount = Math.toIntExact( repos.stream( ).filter( queryFilter ).count( ) ); + return PagedResult.of( totalCount, offset, limit, repos.stream( ).filter( queryFilter ).sorted( comparator ) + .map(mr -> MavenManagedRepository.of(mr)).skip( offset ).limit( limit ).collect( Collectors.toList( ) ) ); } catch (ArithmeticException e) { log.error( "Invalid number of repositories detected." ); throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.INVALID_RESULT_SET_ERROR ) ); } - catch ( RepositoryAdminException e ) - { - e.printStackTrace( ); - } - return null; - } @Override public MavenManagedRepository getManagedRepository( String repositoryId ) throws ArchivaRestServiceException { - return null; + ManagedRepository repo = repositoryRegistry.getManagedRepository( repositoryId ); + if (repo==null) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, repositoryId ), 404 ); + } + if (repo.getType()!=RepositoryType.MAVEN) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_WRONG_TYPE, repositoryId, repo.getType().name() ), 404 ); + } + return MavenManagedRepository.of( repo ); } @Override public Response deleteManagedRepository( String repositoryId, boolean deleteContent ) throws ArchivaRestServiceException { - return null; + ManagedRepository repo = repositoryRegistry.getManagedRepository( repositoryId ); + if (repo==null) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, repositoryId ), 404 ); + } + if (repo.getType()!=RepositoryType.MAVEN) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_WRONG_TYPE, repositoryId, repo.getType().name() ), 404 ); + } + try + { + managedRepositoryAdmin.deleteManagedRepository( repositoryId, getAuditInformation( ), deleteContent ); + return Response.ok( ).build( ); + } + catch ( RepositoryAdminException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_DELETE_FAILED, e.getMessage( ) ) ); + } + } + + private org.apache.archiva.admin.model.beans.ManagedRepository convert(MavenManagedRepository repository) { + org.apache.archiva.admin.model.beans.ManagedRepository repoBean = new org.apache.archiva.admin.model.beans.ManagedRepository( ); + repoBean.setId( repository.getId( ) ); + repoBean.setName( repository.getName() ); + repoBean.setDescription( repository.getDescription() ); + repoBean.setBlockRedeployments( repository.isBlocksRedeployments() ); + repoBean.setCronExpression( repository.getSchedulingDefinition() ); + repoBean.setLocation( repository.getLocation() ); + repoBean.setReleases( repository.getReleaseSchemes().contains( ReleaseScheme.RELEASE.name() ) ); + repoBean.setSnapshots( repository.getReleaseSchemes().contains( ReleaseScheme.SNAPSHOT.name() ) ); + repoBean.setScanned( repository.isScanned() ); + repoBean.setDeleteReleasedSnapshots( repository.isDeleteSnapshotsOfRelease() ); + repoBean.setSkipPackedIndexCreation( repository.isSkipPackedIndexCreation() ); + repoBean.setRetentionCount( repository.getRetentionCount() ); + repoBean.setRetentionPeriod( repository.getRetentionPeriod().getDays() ); + repoBean.setIndexDirectory( repository.getIndexPath() ); + repoBean.setPackedIndexDirectory( repository.getPackedIndexPath() ); + repoBean.setLayout( repository.getLayout() ); + repoBean.setType( RepositoryType.MAVEN.name( ) ); + return repoBean; } @Override public MavenManagedRepository addManagedRepository( MavenManagedRepository managedRepository ) throws ArchivaRestServiceException { - return null; + final String repoId = managedRepository.getId( ); + if ( StringUtils.isEmpty( repoId ) ) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_INVALID_ID, repoId ), 422 ); + } + Repository repo = repositoryRegistry.getRepository( repoId ); + if (repo!=null) { + httpServletResponse.setHeader( "Location", uriInfo.getAbsolutePathBuilder( ).path( repoId ).build( ).toString( ) ); + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ID_EXISTS, repoId ), 303 ); + } + try + { + managedRepositoryAdmin.addManagedRepository( convert( managedRepository ), managedRepository.isHasStagingRepository(), getAuditInformation() ); + httpServletResponse.setStatus( 201 ); + return MavenManagedRepository.of( repositoryRegistry.getManagedRepository( repoId ) ); + } + catch ( RepositoryAdminException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ADMIN_ERROR, e.getMessage( ) ) ); + } } @Override - public MavenManagedRepository updateManagedRepository( MavenManagedRepository managedRepository ) throws ArchivaRestServiceException + public MavenManagedRepository updateManagedRepository( final String repositoryId, final MavenManagedRepositoryUpdate managedRepository ) throws ArchivaRestServiceException { - return null; + org.apache.archiva.admin.model.beans.ManagedRepository repo = convert( managedRepository ); + try + { + managedRepositoryAdmin.updateManagedRepository( repo, managedRepository.isHasStagingRepository( ), getAuditInformation( ), managedRepository.isResetStats( ) ); + ManagedRepository newRepo = repositoryRegistry.getManagedRepository( managedRepository.getId( ) ); + if (newRepo==null) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_UPDATE_FAILED, repositoryId ) ); + } + return MavenManagedRepository.of( newRepo ); + } + catch ( RepositoryAdminException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ADMIN_ERROR, e.getMessage( ) ) ); + } } @Override public FileInfo getFileStatus( String repositoryId, String fileLocation ) throws ArchivaRestServiceException { - return null; + ManagedRepository repo = repositoryRegistry.getManagedRepository( repositoryId ); + if (repo==null) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, repositoryId ), 404 ); + } + try + { + ContentItem contentItem = repo.getContent( ).toItem( fileLocation ); + if (contentItem.getAsset( ).exists( )) { + return FileInfo.of( contentItem.getAsset( ) ); + } else { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_NOT_FOUND, repositoryId, fileLocation ), 404 ); + } + } + catch ( LayoutException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_LAYOUT_ERROR, e.getMessage( ) ) ); + } } @Override public Response copyArtifact( String srcRepositoryId, String dstRepositoryId, String path ) throws ArchivaRestServiceException { + final AuditInformation auditInformation = getAuditInformation( ); + final String userName = auditInformation.getUser( ).getUsername( ); + if ( StringUtils.isEmpty( userName ) ) + { + httpServletResponse.setHeader( "WWW-Authenticate", "Bearer realm=\"archiva\"" ); + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.NOT_AUTHENTICATED ), 401 ); + } ManagedRepository srcRepo = repositoryRegistry.getManagedRepository( srcRepositoryId ); if (srcRepo==null) { throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, srcRepositoryId ), 404 ); @@ -128,17 +278,89 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit if (dstRepo==null) { throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, dstRepositoryId ), 404 ); } - if (dstRepo.getAsset( path ).exists()) { - throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_EXISTS_AT_DEST, path ) ); + checkAuthority( auditInformation.getUser().getUsername(), srcRepositoryId, dstRepositoryId ); + try + { + ContentItem srcItem = srcRepo.getContent( ).toItem( path ); + ContentItem dstItem = dstRepo.getContent( ).toItem( path ); + if (!srcItem.getAsset().exists()){ + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_NOT_FOUND, srcRepositoryId, path ), 404 ); + } + if (dstItem.getAsset().exists()) { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_EXISTS_AT_DEST, srcRepositoryId, path ), 400 ); + } + FsStorageUtil.copyAsset( srcItem.getAsset( ), dstItem.getAsset( ), true ); + } + catch ( LayoutException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_LAYOUT_ERROR, e.getMessage() ) ); + } + catch ( IOException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_COPY_ERROR, e.getMessage() ) ); + } + return Response.ok( ).build(); + } + + private void checkAuthority(final String userName, final String srcRepositoryId, final String dstRepositoryId ) throws ArchivaRestServiceException { + User user = null; + try + { + user = securitySystem.getUserManager().findUser( userName ); + } + catch ( UserNotFoundException e ) + { + httpServletResponse.setHeader( "WWW-Authenticate", "Bearer realm=\"archiva\"" ); + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.USER_NOT_FOUND, userName ), 401 ); + } + catch ( UserManagerException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.USER_MANAGER_ERROR, e.getMessage( ) ) ); + } + + // check karma on source : read + AuthenticationResult authn = new AuthenticationResult( true, userName, null ); + SecuritySession securitySession = new DefaultSecuritySession( authn, user ); + try + { + boolean authz = + securitySystem.isAuthorized( securitySession, OPERATION_REPOSITORY_ACCESS, + srcRepositoryId ); + if ( !authz ) + { + throw new ArchivaRestServiceException(ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, srcRepositoryId, OPERATION_REPOSITORY_ACCESS ), 403); + } + } + catch ( AuthorizationException e ) + { + log.error( "Error reading permission: {}", e.getMessage(), e ); + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.AUTHORIZATION_ERROR, e.getMessage() ), 403); + } + + // check karma on target: write + try + { + boolean authz = + securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD, + dstRepositoryId ); + if ( !authz ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, dstRepositoryId, OPERATION_REPOSITORY_UPLOAD ) ); + } + } + catch ( AuthorizationException e ) + { + log.error( "Error reading permission: {}", e.getMessage(), e ); + throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.AUTHORIZATION_ERROR, e.getMessage() ), 403); } - return null; } @Override public Response deleteArtifact( String repositoryId, String path ) throws ArchivaRestServiceException { + return null; } |