]> source.dussan.org Git - archiva.git/commitdiff
Adding additional operations for access control
authorMartin Stockhammer <martin_s@apache.org>
Mon, 20 Dec 2021 21:28:33 +0000 (22:28 +0100)
committerMartin Stockhammer <martin_s@apache.org>
Mon, 20 Dec 2021 21:28:33 +0000 (22:28 +0100)
archiva-modules/archiva-base/archiva-security-common/src/main/java/org/apache/archiva/security/common/ArchivaRoleConstants.java
archiva-modules/archiva-base/archiva-security-common/src/main/resources/META-INF/redback/redback.xml
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/v2/svc/maven/MavenManagedRepositoryService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/DefaultMavenManagedRepositoryService.java [deleted file]
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/maven/DefaultMavenManagedRepositoryService.java [new file with mode: 0644]

index 5be7c8b0e8c5db679c0cde80d97249e878b572e6..e7a04e8652860304a00d70d7ae453a4e5c0f4a16 100644 (file)
@@ -64,7 +64,7 @@ public class ArchivaRoleConstants
 
     /**
      * Permission to add a repository
-     * Scope: application
+     * Scope: global
      */
     public static final String OPERATION_ADD_REPOSITORY = "archiva-add-repository";
 
@@ -118,10 +118,15 @@ public class ArchivaRoleConstants
 
     /**
      * Permission to upload a file to the upload workspace
-     * Scope: application
+     * Scope: global
      */
     public static final String OPERATION_FILE_UPLOAD = "archiva-upload-file";
 
+    /**
+     * Permission to list all available repositories
+     * Scope: global
+     */
+    public static final String OPERATION_LIST_REPOSITORIES = "archiva-list-repositories";
 
 
     public static final String OPERATION_MERGE_REPOSITORY = "archiva-merge-repository";
@@ -138,7 +143,8 @@ public class ArchivaRoleConstants
     public static final String TEMPLATE_SYSTEM_ADMIN = "archiva-system-administrator";
     
     public static final String TEMPLATE_GUEST = "archiva-guest";
-    
+
+
     public static String toRepositoryObserverRoleName( String repoId )
     {
         return REPOSITORY_OBSERVER_ROLE_PREFIX + " - " + repoId;
index e236e8218eaff4e300bacd7fa365ef441c2f9a31..35599374df8cf31564471839a33abf47c9c480f6 100644 (file)
           <name>archiva-access-reports</name>
           <description>Access Archiva Reports</description>
         </operation>
+        <operation>
+          <id>archiva-list-repositories</id>
+          <name>archiva-list-repositories</name>
+          <description>List all repositories</description>
+        </operation>
         <operation>
           <id>archiva-add-repository</id>
           <name>archiva-add-repository</name>
           <namePrefix>Repository Manager</namePrefix>
           <assignable>true</assignable>
           <permissions>
+            <permission>
+              <id>archiva-list-repositories</id>
+              <name>Archiva List Repositories</name>
+              <operation>archiva-list-repositories</operation>
+              <resource>global</resource>
+            </permission>
             <permission>
               <id>archiva-delete-namespace</id>
               <name>Archiva Delete Namespace (GroupId)</name>
index 98ac75419f7da7c0fda0faa1b733ca1c74ce51be..f8e3f314cd22c931e9e79bc46d51c2ee3a2932b9 100644 (file)
@@ -32,7 +32,6 @@ import org.apache.archiva.rest.api.v2.model.MavenManagedRepository;
 import org.apache.archiva.rest.api.v2.model.MavenManagedRepositoryUpdate;
 import org.apache.archiva.rest.api.v2.svc.ArchivaRestError;
 import org.apache.archiva.rest.api.v2.svc.ArchivaRestServiceException;
-import org.apache.archiva.security.common.ArchivaRoleConstants;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -53,12 +52,25 @@ import static org.apache.archiva.rest.api.v2.svc.RestConfiguration.DEFAULT_PAGE_
 import static org.apache.archiva.security.common.ArchivaRoleConstants.*;
 
 /**
+ *
  * Service interface for update, delete, add of Managed Maven Repositories
  *
+ * The add, delete, update methods for a repository use "/{id}" with the classical CRUD actions.
+ * Where {id} is the repository ID.
+ *
+ * There are subpaths for certain repository management functions:
+ * <ul>
+ * <li>{@code /{id}/path/{groupsection1/groupsection2/... }/{project}/{version}/{artifact-file}}
+ *  is used for accessing artifacts and directories by their repository path</li>
+ * <li>{@code /{id}/co/{groupid}/{artifactid}/{version} } is used to access Maven artifacts by their coordinates.
+ *  Which means, {groupid} is a '.' separated string.
+ * </li>
+ * </ul>
+ *
  * @author Martin Stockhammer <martin_s@apache.org>
  * @since 3.0
  */
-@Schema( name = "ManagedRepositoryService", description = "Managing and configuration of managed repositories" )
+@Schema( name = "MavenManagedRepositoryService", description = "Managing and configuration of managed maven repositories" )
 @Path( "repositories/maven/managed" )
 @Tag(name = "v2")
 @Tag(name = "v2/Repositories")
@@ -67,7 +79,7 @@ public interface MavenManagedRepositoryService
     @Path( "" )
     @GET
     @Produces( {APPLICATION_JSON} )
-    @RedbackAuthorization( permissions = OPERATION_MANAGE_CONFIGURATION )
+    @RedbackAuthorization( permissions = { OPERATION_MANAGE_CONFIGURATION, OPERATION_LIST_REPOSITORIES } )
     @Operation( summary = "Returns all managed repositories.",
         parameters = {
             @Parameter( name = "q", description = "Search term" ),
@@ -79,7 +91,11 @@ public interface MavenManagedRepositoryService
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
+            ),
+            @SecurityRequirement(
+                name = OPERATION_LIST_REPOSITORIES
             )
+
         },
         responses = {
             @ApiResponse( responseCode = "200",
@@ -159,7 +175,8 @@ public interface MavenManagedRepositoryService
         }
     )
     Response deleteManagedRepository( @PathParam( "id" ) String repositoryId,
-                                      @QueryParam( "deleteContent" ) boolean deleteContent )
+                                      @DefaultValue( "false" )
+                                      @QueryParam( "deleteContent" ) Boolean deleteContent )
         throws ArchivaRestServiceException;
 
 
@@ -243,7 +260,7 @@ public interface MavenManagedRepositoryService
         permissions = { OPERATION_MANAGE_CONFIGURATION, OPERATION_READ_REPOSITORY},
         resource = "{id}"
     )
-    @Operation( summary = "Returns the status of a given file in the repository",
+    @Operation( summary = "Returns the status of a given artifact file in the repository",
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
@@ -277,7 +294,7 @@ public interface MavenManagedRepositoryService
     @POST
     @Produces({APPLICATION_JSON})
     @RedbackAuthorization (noPermission = true)
-    @Operation( summary = "Copies a artifact from the source repository to the destination repository",
+    @Operation( summary = "Copies a artifact from the source repository to the destination repository with the same path",
         security = {
             @SecurityRequirement(
                 name = OPERATION_READ_REPOSITORY,
@@ -315,7 +332,7 @@ public interface MavenManagedRepositoryService
         permissions = { OPERATION_MANAGE_CONFIGURATION, OPERATION_DELETE_ARTIFACT },
         resource = "{id}"
     )
-    @Operation( summary = "Deletes a artifact in the repository.",
+    @Operation( summary = "Deletes a artifact from the repository.",
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
@@ -339,14 +356,14 @@ public interface MavenManagedRepositoryService
     Response deleteArtifact( @PathParam( "id" ) String repositoryId, @PathParam( "path" ) String path )
         throws ArchivaRestServiceException;
 
-    @Path ( "{id}/co/{group}/{project}/{version}" )
+    @Path ( "{id}/co/{groupid}/{artifactid}/{version}" )
     @DELETE
     @Produces ({ MediaType.APPLICATION_JSON })
     @RedbackAuthorization (
         permissions = { OPERATION_MANAGE_CONFIGURATION, OPERATION_DELETE_VERSION},
         resource = "{id}"
     )
-    @Operation( summary = "Removes a version tree in the repository",
+    @Operation( summary = "Removes a version and all its content from the repository",
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
@@ -368,16 +385,16 @@ public interface MavenManagedRepositoryService
         }
     )
     Response removeProjectVersion( @PathParam ( "id" ) String repositoryId,
-                                   @PathParam ( "group" ) String namespace, @PathParam ( "project" ) String projectId,
+                                   @PathParam ( "groupid" ) String namespace, @PathParam ( "artifactid" ) String projectId,
                                    @PathParam ( "version" ) String version )
         throws org.apache.archiva.rest.api.services.ArchivaRestServiceException;
 
 
-    @Path ( "{id}/co/{group}/{project}" )
+    @Path ( "{id}/co/{groupid}/{artifactid}" )
     @DELETE
     @Produces ({ MediaType.APPLICATION_JSON })
     @RedbackAuthorization (noPermission = true)
-    @Operation( summary = "Removes a project tree in the repository",
+    @Operation( summary = "Removes a artifact and all its versions from the repository",
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
@@ -394,21 +411,21 @@ public interface MavenManagedRepositoryService
             ),
             @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to delete in repositories",
                 content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ),
-            @ApiResponse( responseCode = "404", description = "The managed repository with this id does not exist. Or the project does not exist.",
+            @ApiResponse( responseCode = "404", description = "The managed repository with this id does not exist. Or the artifact does not exist.",
                 content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) )
         }
     )
-    Response deleteProject( @PathParam ("id") String repositoryId, @PathParam ( "group" ) String namespace, @PathParam ( "project" ) String projectId )
+    Response deleteProject( @PathParam ("id") String repositoryId, @PathParam ( "groupid" ) String namespace, @PathParam ( "artifactid" ) String projectId )
         throws org.apache.archiva.rest.api.services.ArchivaRestServiceException;
 
-    @Path ( "{id}/co/{namespace}" )
+    @Path ( "{id}/co/{groupid}" )
     @DELETE
     @Produces ({ MediaType.APPLICATION_JSON })
     @RedbackAuthorization (
         permissions = { OPERATION_MANAGE_CONFIGURATION, OPERATION_DELETE_NAMESPACE },
         resource = "{id}"
     )
-    @Operation( summary = "Removes a namespace tree in the repository",
+    @Operation( summary = "Removes a group and all subfolders from the repository",
         security = {
             @SecurityRequirement(
                 name = OPERATION_MANAGE_CONFIGURATION
@@ -424,11 +441,11 @@ public interface MavenManagedRepositoryService
             ),
             @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to delete namespaces in repositories",
                 content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ),
-            @ApiResponse( responseCode = "404", description = "The managed repository with this id does not exist. Or the namespace does not exist.",
+            @ApiResponse( responseCode = "404", description = "The managed repository with this id does not exist. Or the groupid does not exist.",
                 content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) )
         }
     )
-    Response deleteNamespace( @PathParam ("id") String repositoryId, @PathParam ( "namespace" ) String namespace )
+    Response deleteNamespace( @PathParam ("id") String repositoryId, @PathParam ( "groupid" ) String namespace )
         throws org.apache.archiva.rest.api.services.ArchivaRestServiceException;
 
 }
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/DefaultMavenManagedRepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/DefaultMavenManagedRepositoryService.java
deleted file mode 100644 (file)
index 8296d04..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-package org.apache.archiva.rest.v2.svc;
-/*
- * 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.
- */
-
-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.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.FsStorageUtil;
-import org.apache.archiva.rest.api.v2.model.FileInfo;
-import org.apache.archiva.rest.api.v2.model.MavenManagedRepository;
-import org.apache.archiva.rest.api.v2.model.MavenManagedRepositoryUpdate;
-import org.apache.archiva.rest.api.v2.svc.ArchivaRestServiceException;
-import org.apache.archiva.rest.api.v2.svc.ErrorKeys;
-import org.apache.archiva.rest.api.v2.svc.ErrorMessage;
-import org.apache.archiva.rest.api.v2.svc.maven.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.servlet.http.HttpServletResponse;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-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_READ_REPOSITORY;
-import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_ADD_ARTIFACT;
-
-/**
- * @author Martin Stockhammer <martin_s@apache.org>
- */
-@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<ManagedRepository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} );
-    static
-    {
-        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( SecuritySystem securitySystem,
-                                                 RepositoryRegistry repositoryRegistry,
-                                                 ManagedRepositoryAdmin managedRepositoryAdmin )
-    {
-        this.securitySystem = securitySystem;
-        this.repositoryRegistry = repositoryRegistry;
-        this.managedRepositoryAdmin = managedRepositoryAdmin;
-    }
-
-    protected AuditInformation getAuditInformation( )
-    {
-        RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get( );
-        User user;
-        String remoteAddr;
-        if (redbackRequestInformation==null) {
-            user = null;
-            remoteAddr = null;
-        } else
-        {
-            user = redbackRequestInformation.getUser( );
-            remoteAddr = redbackRequestInformation.getRemoteAddr( );
-        }
-        return new AuditInformation( user, remoteAddr );
-    }
-
-    @Override
-    public PagedResult<MavenManagedRepository> getManagedRepositories( final String searchTerm, final Integer offset,
-                                                                       final Integer limit, final List<String> orderBy,
-                                                                       final String order ) throws ArchivaRestServiceException
-    {
-        try
-        {
-            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 ) );
-        }
-    }
-
-    @Override
-    public MavenManagedRepository getManagedRepository( String repositoryId ) throws ArchivaRestServiceException
-    {
-        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
-    {
-        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
-    {
-        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( final String repositoryId, final MavenManagedRepositoryUpdate managedRepository ) throws ArchivaRestServiceException
-    {
-        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
-    {
-        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 );
-        }
-        ManagedRepository dstRepo = repositoryRegistry.getManagedRepository( dstRepositoryId );
-        if (dstRepo==null) {
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, dstRepositoryId ), 404 );
-        }
-        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_READ_REPOSITORY,
-                    srcRepositoryId );
-            if ( !authz )
-            {
-                throw new ArchivaRestServiceException(ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, srcRepositoryId, OPERATION_READ_REPOSITORY ), 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_ADD_ARTIFACT,
-                    dstRepositoryId );
-            if ( !authz )
-            {
-                throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, dstRepositoryId, OPERATION_ADD_ARTIFACT ) );
-            }
-        }
-        catch ( AuthorizationException e )
-        {
-            log.error( "Error reading permission: {}", e.getMessage(), e );
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.AUTHORIZATION_ERROR, e.getMessage() ), 403);
-        }
-
-
-    }
-
-    @Override
-    public Response deleteArtifact( String repositoryId, String path ) throws ArchivaRestServiceException
-    {
-
-        return null;
-    }
-
-
-    @Override
-    public Response removeProjectVersion( String repositoryId, String namespace, String projectId, String version ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
-    {
-        return null;
-    }
-
-    @Override
-    public Response deleteProject( String repositoryId, String namespace, String projectId ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
-    {
-        return null;
-    }
-
-    @Override
-    public Response deleteNamespace( String repositoryId, String namespace ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
-    {
-        return null;
-    }
-
-}
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/maven/DefaultMavenManagedRepositoryService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/maven/DefaultMavenManagedRepositoryService.java
new file mode 100644 (file)
index 0000000..5e3c11a
--- /dev/null
@@ -0,0 +1,389 @@
+package org.apache.archiva.rest.v2.svc.maven;
+/*
+ * 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.
+ */
+
+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.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.FsStorageUtil;
+import org.apache.archiva.rest.api.v2.model.FileInfo;
+import org.apache.archiva.rest.api.v2.model.MavenManagedRepository;
+import org.apache.archiva.rest.api.v2.model.MavenManagedRepositoryUpdate;
+import org.apache.archiva.rest.api.v2.svc.ArchivaRestServiceException;
+import org.apache.archiva.rest.api.v2.svc.ErrorKeys;
+import org.apache.archiva.rest.api.v2.svc.ErrorMessage;
+import org.apache.archiva.rest.api.v2.svc.maven.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.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+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_READ_REPOSITORY;
+import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_ADD_ARTIFACT;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+@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<ManagedRepository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} );
+    static
+    {
+        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( SecuritySystem securitySystem,
+                                                 RepositoryRegistry repositoryRegistry,
+                                                 ManagedRepositoryAdmin managedRepositoryAdmin )
+    {
+        this.securitySystem = securitySystem;
+        this.repositoryRegistry = repositoryRegistry;
+        this.managedRepositoryAdmin = managedRepositoryAdmin;
+    }
+
+    protected AuditInformation getAuditInformation( )
+    {
+        RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get( );
+        User user;
+        String remoteAddr;
+        if (redbackRequestInformation==null) {
+            user = null;
+            remoteAddr = null;
+        } else
+        {
+            user = redbackRequestInformation.getUser( );
+            remoteAddr = redbackRequestInformation.getRemoteAddr( );
+        }
+        return new AuditInformation( user, remoteAddr );
+    }
+
+    @Override
+    public PagedResult<MavenManagedRepository> getManagedRepositories( final String searchTerm, final Integer offset,
+                                                                       final Integer limit, final List<String> orderBy,
+                                                                       final String order ) throws ArchivaRestServiceException
+    {
+        try
+        {
+            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 ) );
+        }
+    }
+
+    @Override
+    public MavenManagedRepository getManagedRepository( String repositoryId ) throws ArchivaRestServiceException
+    {
+        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
+    {
+        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
+    {
+        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( final String repositoryId, final MavenManagedRepositoryUpdate managedRepository ) throws ArchivaRestServiceException
+    {
+        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
+    {
+        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 );
+        }
+        ManagedRepository dstRepo = repositoryRegistry.getManagedRepository( dstRepositoryId );
+        if (dstRepo==null) {
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, dstRepositoryId ), 404 );
+        }
+        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_READ_REPOSITORY,
+                    srcRepositoryId );
+            if ( !authz )
+            {
+                throw new ArchivaRestServiceException(ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, srcRepositoryId, OPERATION_READ_REPOSITORY ), 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_ADD_ARTIFACT,
+                    dstRepositoryId );
+            if ( !authz )
+            {
+                throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PERMISSION_REPOSITORY_DENIED, dstRepositoryId, OPERATION_ADD_ARTIFACT ) );
+            }
+        }
+        catch ( AuthorizationException e )
+        {
+            log.error( "Error reading permission: {}", e.getMessage(), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.AUTHORIZATION_ERROR, e.getMessage() ), 403);
+        }
+
+
+    }
+
+    @Override
+    public Response deleteArtifact( String repositoryId, String path ) throws ArchivaRestServiceException
+    {
+
+        return null;
+    }
+
+
+    @Override
+    public Response removeProjectVersion( String repositoryId, String namespace, String projectId, String version ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
+    {
+        return null;
+    }
+
+    @Override
+    public Response deleteProject( String repositoryId, String namespace, String projectId ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
+    {
+        return null;
+    }
+
+    @Override
+    public Response deleteNamespace( String repositoryId, String namespace ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException
+    {
+        return null;
+    }
+
+}