You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by oc...@apache.org on 2009/02/20 09:50:36 UTC

svn commit: r746183 - in /archiva/trunk/archiva-modules: archiva-database/src/main/java/org/apache/maven/archiva/database/updater/ archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/ archiva-web/archiva-security/src/main/resou...

Author: oching
Date: Fri Feb 20 08:50:35 2009
New Revision: 746183

URL: http://svn.apache.org/viewvc?rev=746183&view=rev
Log:
[MRM-913]
submitted by Jevica Arianne Zurbano
o patch does the following:
  - deletes artifacts
  - cleans up database
  - executes scan to reflect changes when browsing repository
  - updates metadata
  - updates audit.log
  - 'Delete Artifact' added in navigation and is displayed for users with repository manager role
  - only allows manager of the repository to delete artifacts from it

o additional tweaks to the submitted patch:
  - removed catch for NPE in DeleteArtifactAction
  - migrated jsps & action class to struts 2
  - moved invocation of cleanup consumers in DeleteArtifactAction to DatabaseConsumers (added new method for this in DatabaseConsumers)
  - applied formatting

Added:
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/DeleteArtifactAction.java
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/DeleteArtifactAction-validation.xml
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/deleteArtifact.jsp
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/deleteArtifactForm.jspf
Modified:
    archiva/trunk/archiva-modules/archiva-database/src/main/java/org/apache/maven/archiva/database/updater/DatabaseConsumers.java
    archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/DefaultUserRepositories.java
    archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/UserRepositories.java
    archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/resources/META-INF/redback/redback.xml
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/decorators/default.jsp
    archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/rss/UserRepositoriesStub.java

Modified: archiva/trunk/archiva-modules/archiva-database/src/main/java/org/apache/maven/archiva/database/updater/DatabaseConsumers.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-database/src/main/java/org/apache/maven/archiva/database/updater/DatabaseConsumers.java?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-database/src/main/java/org/apache/maven/archiva/database/updater/DatabaseConsumers.java (original)
+++ archiva/trunk/archiva-modules/archiva-database/src/main/java/org/apache/maven/archiva/database/updater/DatabaseConsumers.java Fri Feb 20 08:50:35 2009
@@ -25,11 +25,15 @@
 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
 import org.apache.maven.archiva.configuration.DatabaseScanningConfiguration;
 import org.apache.maven.archiva.consumers.functors.PermanentConsumerPredicate;
+import org.apache.maven.archiva.model.ArchivaArtifact;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -40,6 +44,8 @@
 public class DatabaseConsumers
     implements ApplicationContextAware
 {    
+    private Logger log = LoggerFactory.getLogger( DatabaseConsumers.class );
+    
     private ArchivaConfiguration archivaConfiguration;
 
     private Predicate selectedCleanupConsumers;
@@ -149,4 +155,37 @@
     {
         return new ArrayList( applicationContext.getBeansOfType( DatabaseCleanupConsumer.class ).values() );
     }
+    
+    /**
+     * Execute the cleanup consumers to cleanup the specified artifact from the database and index.
+     * 
+     * @param artifact
+     */
+    public void executeCleanupConsumer( ArchivaArtifact artifact )
+    {
+        List consumers = getSelectedCleanupConsumers();
+        Iterator it = consumers.iterator();
+        while ( it.hasNext() )
+        {
+            ArchivaArtifactConsumer consumer = (ArchivaArtifactConsumer) it.next();
+            consumer.beginScan();
+        }
+        
+        if ( CollectionUtils.isEmpty( consumers ) )
+        {
+            log.warn( "There are no selected consumers for artifact cleanup." );
+            return;
+        }
+        
+        ProcessArchivaArtifactClosure processArtifactClosure = new ProcessArchivaArtifactClosure();
+        processArtifactClosure.setArtifact( artifact );
+        
+        CollectionUtils.forAllDo( consumers, processArtifactClosure );
+        
+        while ( it.hasNext() )
+        {
+            ArchivaArtifactConsumer consumer = (ArchivaArtifactConsumer) it.next();
+            consumer.completeScan();
+        }
+    }
 }

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/DefaultUserRepositories.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/DefaultUserRepositories.java?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/DefaultUserRepositories.java (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/DefaultUserRepositories.java Fri Feb 20 08:50:35 2009
@@ -20,13 +20,19 @@
  */
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
+import org.apache.maven.archiva.security.ArchivaRoleConstants;
 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
 import org.codehaus.plexus.redback.authorization.AuthorizationException;
 import org.codehaus.plexus.redback.rbac.RBACManager;
+import org.codehaus.plexus.redback.rbac.RbacObjectNotFoundException;
+import org.codehaus.plexus.redback.rbac.RbacManagerException;
+import org.codehaus.plexus.redback.rbac.Role;
 import org.codehaus.plexus.redback.role.RoleManager;
 import org.codehaus.plexus.redback.role.RoleManagerException;
 import org.codehaus.plexus.redback.system.DefaultSecuritySession;
@@ -161,4 +167,47 @@
             throw new ArchivaSecurityException( e.getMessage() );
         }
     }
+    
+    public boolean isAuthorizedToDeleteArtifacts( String principal, String repoId )
+        throws RbacManagerException, RbacObjectNotFoundException
+    {
+        boolean isAuthorized = false;
+        String delimiter = " - ";
+        
+        try
+        {
+            Collection roleList = rbacManager.getEffectivelyAssignedRoles( principal );
+            
+            Iterator it = roleList.iterator();
+            
+            while ( it.hasNext() )
+            {
+                Role role = (Role) it.next();
+                
+                String roleName = role.getName();
+                
+                if ( roleName.startsWith( ArchivaRoleConstants.REPOSITORY_MANAGER_ROLE_PREFIX ) )
+                {
+                    int delimiterIndex = roleName.indexOf( delimiter );
+                    String resourceName = roleName.substring( delimiterIndex + delimiter.length() );
+                    
+                    if ( resourceName.equals( repoId ) )
+                    {
+                        isAuthorized = true;
+                        break;
+                    }
+                }
+            }
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            throw new RbacObjectNotFoundException( "Unable to find user " + principal + "" );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RbacManagerException( "Unable to get roles for user " + principal + "" );
+        }
+        
+        return isAuthorized;
+    }
 }

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/UserRepositories.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/UserRepositories.java?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/UserRepositories.java (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/java/org/apache/maven/archiva/security/UserRepositories.java Fri Feb 20 08:50:35 2009
@@ -19,6 +19,9 @@
  * under the License.
  */
 
+import org.codehaus.plexus.redback.rbac.RbacObjectNotFoundException;
+import org.codehaus.plexus.redback.rbac.RbacManagerException;
+
 import java.util.List;
 
 /**
@@ -60,5 +63,17 @@
      */
     public boolean isAuthorizedToUploadArtifacts( String principal, String repoId)
         throws PrincipalNotFoundException, ArchivaSecurityException;
+     
+    /**
+     * Check if user is authorized to delete artifacts in the repository.
+     * 
+     * @param principal
+     * @param repoId
+     * @return
+     * @throws RbacManagerException
+     * @throws RbacObjectNotFoundException
+     */
+    public boolean isAuthorizedToDeleteArtifacts( String principal, String repoId )
+        throws RbacManagerException, RbacObjectNotFoundException;
     
 }

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/resources/META-INF/redback/redback.xml
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/resources/META-INF/redback/redback.xml?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/resources/META-INF/redback/redback.xml (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-security/src/main/resources/META-INF/redback/redback.xml Fri Feb 20 08:50:35 2009
@@ -25,6 +25,11 @@
           <name>archiva-run-indexer</name>
           <description>Run Archiva Indexer</description>
         </operation>
+         <operation>
+          <id>archiva-delete-artifact</id>
+          <name>archiva-delete-artifact</name>
+          <description>Delete Artifact</description>
+        </operation>
         <operation>
           <id>archiva-access-reports</id>
           <name>archiva-access-reports</name>
@@ -180,6 +185,13 @@
           <namePrefix>Repository Manager</namePrefix>
           <assignable>true</assignable>
           <permissions>
+             <permission>
+              <id>archiva-delete-artifact</id>
+              <name>Delete Artifact</name>
+              <operation>archiva-delete-artifact</operation>
+              <resource>global</resource>
+              <permanent>true</permanent>
+            </permission>
             <permission>
               <id>archiva-edit-repository</id>
               <name>Archiva Edit Repository</name>

Added: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/DeleteArtifactAction.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/DeleteArtifactAction.java?rev=746183&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/DeleteArtifactAction.java (added)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/DeleteArtifactAction.java Fri Feb 20 08:50:35 2009
@@ -0,0 +1,426 @@
+package org.apache.maven.archiva.web.action;
+
+/*
+ * 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 java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.archiva.checksum.ChecksumAlgorithm;
+import org.apache.archiva.checksum.ChecksummedFile;
+import org.apache.maven.archiva.common.utils.VersionComparator;
+import org.apache.maven.archiva.common.utils.VersionUtil;
+import org.apache.maven.archiva.configuration.ArchivaConfiguration;
+import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
+import org.apache.maven.archiva.database.updater.DatabaseConsumers;
+import org.apache.maven.archiva.database.ArchivaDatabaseException;
+import org.apache.maven.archiva.database.ArtifactDAO;
+import org.apache.maven.archiva.database.constraints.ArtifactVersionsConstraint;
+import org.apache.maven.archiva.model.ArchivaArtifact;
+import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
+import org.apache.maven.archiva.model.VersionedReference;
+import org.apache.maven.archiva.repository.audit.Auditable;
+import org.apache.maven.archiva.repository.audit.AuditEvent;
+import org.apache.maven.archiva.repository.audit.AuditListener;
+import org.apache.maven.archiva.repository.metadata.MetadataTools;
+import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
+import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
+import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
+import org.apache.maven.archiva.repository.ContentNotFoundException;
+import org.apache.maven.archiva.repository.RepositoryException;
+import org.apache.maven.archiva.repository.RepositoryNotFoundException;
+import org.apache.maven.archiva.repository.ManagedRepositoryContent;
+import org.apache.maven.archiva.repository.RepositoryContentFactory;
+import org.apache.maven.archiva.security.ArchivaXworkUser;
+import org.apache.maven.archiva.security.UserRepositories;
+import org.codehaus.plexus.redback.rbac.RbacManagerException;
+
+import org.apache.struts2.ServletActionContext;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.Preparable;
+import com.opensymphony.xwork2.Validateable;
+
+/**
+ * Delete an artifact. Metadata will be updated if one exists, otherwise it would be created.
+ * 
+ * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="deleteArtifactAction"
+ */
+public class DeleteArtifactAction
+    extends PlexusActionSupport
+    implements Validateable, Preparable, Auditable
+{
+    /**
+     * @plexus.requirement
+     */
+    private ArchivaXworkUser archivaXworkUser;
+
+    /**
+     * The groupId of the artifact to be deleted.
+     */
+    private String groupId;
+
+    /**
+     * The artifactId of the artifact to be deleted.
+     */
+    private String artifactId;
+
+    /**
+     * The version of the artifact to be deleted.
+     */
+    private String version;
+
+    /**
+     * The repository where the artifact is to be deleted.
+     */
+    private String repositoryId;
+
+    /**
+     * List of managed repositories to delete from.
+     */
+    private List<String> managedRepos;
+
+    /**
+     * @plexus.requirement
+     */
+    private UserRepositories userRepositories;
+
+    /**
+     * @plexus.requirement role-hint="default"
+     */
+    private ArchivaConfiguration configuration;
+
+    /**
+     * @plexus.requirement
+     */
+    private RepositoryContentFactory repositoryFactory;
+
+    /**
+     * @plexus.requirement role-hint="jdo"
+     */
+    private ArtifactDAO artifactDAO;
+
+    /**
+     * @plexus.requirement 
+     */
+    private DatabaseConsumers databaseConsumers;
+
+    /**
+     * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
+     */
+    private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
+
+    private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[] { ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
+
+    public String getGroupId()
+    {
+        return groupId;
+    }
+
+    public void setGroupId( String groupId )
+    {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId()
+    {
+        return artifactId;
+    }
+
+    public void setArtifactId( String artifactId )
+    {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion( String version )
+    {
+        this.version = version;
+    }
+
+    public String getRepositoryId()
+    {
+        return repositoryId;
+    }
+
+    public void setRepositoryId( String repositoryId )
+    {
+        this.repositoryId = repositoryId;
+    }
+
+    public List<String> getManagedRepos()
+    {
+        return managedRepos;
+    }
+
+    public void setManagedRepos( List<String> managedRepos )
+    {
+        this.managedRepos = managedRepos;
+    }
+
+    public void prepare()
+    {
+        managedRepos = new ArrayList<String>( configuration.getConfiguration().getManagedRepositoriesAsMap().keySet() );
+    }
+
+    public String input()
+    {
+        return INPUT;
+    }
+
+    private void reset()
+    {
+        // reset the fields so the form is clear when 
+        // the action returns to the jsp page
+        groupId = "";
+        artifactId = "";
+        version = "";
+        repositoryId = "";
+    }
+
+    public String doDelete()
+    {
+        try
+        {
+            Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
+
+            TimeZone timezone = TimeZone.getTimeZone( "UTC" );
+            DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
+            fmt.setTimeZone( timezone );
+            String timestamp = fmt.format( lastUpdatedTimestamp );
+
+            ManagedRepositoryConfiguration repoConfig =
+                configuration.getConfiguration().findManagedRepositoryById( repositoryId );
+
+            VersionedReference ref = new VersionedReference();
+            ref.setArtifactId( artifactId );
+            ref.setGroupId( groupId );
+            ref.setVersion( version );
+
+            ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
+
+            String path = repository.toMetadataPath( ref );
+            int index = path.lastIndexOf( '/' );
+            File targetPath = new File( repoConfig.getLocation(), path.substring( 0, index ) );
+
+            if ( !targetPath.exists() )
+            {
+                throw new ContentNotFoundException( groupId + ":" + artifactId + ":" + version );
+            }
+
+            // delete from file system
+            repository.deleteVersion( ref );
+
+            File metadataFile = getMetadata( targetPath.getAbsolutePath() );
+            ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
+
+            updateMetadata( metadata, metadataFile, lastUpdatedTimestamp );
+
+            ArtifactVersionsConstraint constraint =
+                new ArtifactVersionsConstraint( repositoryId, groupId, artifactId, false );
+            List<ArchivaArtifact> artifacts = null;
+
+            try
+            {
+                artifacts = artifactDAO.queryArtifacts( constraint );
+
+                if ( artifacts != null )
+                {
+                    for ( ArchivaArtifact artifact : artifacts )
+                    {
+                        if ( artifact.getVersion().equals( version ) )
+                        {
+                            databaseConsumers.executeCleanupConsumer( artifact );
+                        }
+                    }
+                }
+            }
+            catch ( ArchivaDatabaseException e )
+            {
+                addActionError( "Error occurred while cleaning up database: " + e.getMessage() );
+                return ERROR;
+            }
+
+            String msg =
+                "Artifact \'" + groupId + ":" + artifactId + ":" + version +
+                    "\' was successfully deleted from repository \'" + repositoryId + "\'";
+
+            triggerAuditEvent( getPrincipal(), repositoryId, groupId + ":" + artifactId + ":" + version,
+                               AuditEvent.REMOVE_FILE );
+
+            addActionMessage( msg );
+
+            reset();
+            return SUCCESS;
+        }
+        catch ( ContentNotFoundException e )
+        {
+            addActionError( "Artifact does not exist: " + e.getMessage() );
+            return ERROR;
+        }
+        catch ( RepositoryNotFoundException e )
+        {
+            addActionError( "Target repository cannot be found: " + e.getMessage() );
+            return ERROR;
+        }
+        catch ( RepositoryException e )
+        {
+            addActionError( "Repository exception: " + e.getMessage() );
+            return ERROR;
+        }
+    }
+
+    private String getPrincipal()
+    {
+        return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
+    }
+
+    private File getMetadata( String targetPath )
+    {
+        String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
+
+        return new File( artifactPath, MetadataTools.MAVEN_METADATA );
+    }
+
+    private ArchivaRepositoryMetadata getMetadata( File metadataFile )
+        throws RepositoryMetadataException
+    {
+        ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
+        if ( metadataFile.exists() )
+        {
+            metadata = RepositoryMetadataReader.read( metadataFile );
+        }
+        return metadata;
+    }
+
+    /**
+     * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion.
+     * 
+     * @param metadata
+     */
+    private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp )
+        throws RepositoryMetadataException
+    {
+        List<String> availableVersions = new ArrayList<String>();
+        String latestVersion = "";
+
+        if ( metadataFile.exists() )
+        {
+            if ( metadata.getAvailableVersions() != null )
+            {
+                availableVersions = metadata.getAvailableVersions();
+
+                if ( availableVersions.size() > 0 )
+                {
+                    Collections.sort( availableVersions, VersionComparator.getInstance() );
+
+                    if ( availableVersions.contains( version ) )
+                    {
+                        availableVersions.remove( availableVersions.indexOf( version ) );
+                    }
+                    if ( availableVersions.size() > 0 )
+                    {
+                        latestVersion = availableVersions.get( availableVersions.size() - 1 );
+                    }
+                }
+            }
+        }
+
+        if ( metadata.getGroupId() == null )
+        {
+            metadata.setGroupId( groupId );
+        }
+        if ( metadata.getArtifactId() == null )
+        {
+            metadata.setArtifactId( artifactId );
+        }
+
+        if ( !VersionUtil.isSnapshot( version ) )
+        {
+            if ( metadata.getReleasedVersion().equals( version ) )
+            {
+                metadata.setReleasedVersion( latestVersion );
+            }
+        }
+
+        metadata.setLatestVersion( latestVersion );
+        metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
+        metadata.setAvailableVersions( availableVersions );
+
+        RepositoryMetadataWriter.write( metadata, metadataFile );
+        ChecksummedFile checksum = new ChecksummedFile( metadataFile );
+        checksum.fixChecksums( algorithms );
+    }
+
+    public void validate()
+    {
+        try
+        {
+            if ( !userRepositories.isAuthorizedToDeleteArtifacts( getPrincipal(), repositoryId ) )
+            {
+                addActionError( "User is not authorized to delete artifacts in repository '" + repositoryId + "'." );
+            }
+
+            if ( ( version.length() > 0 ) && ( !VersionUtil.isVersion( version ) ) )
+            {
+                addActionError( "Invalid version." );
+            }
+        }
+        catch ( RbacManagerException e )
+        {
+            addActionError( e.getMessage() );
+        }
+    }
+
+    public void addAuditListener( AuditListener listener )
+    {
+        this.auditListeners.add( listener );
+    }
+
+    public void clearAuditListeners()
+    {
+        this.auditListeners.clear();
+    }
+
+    public void removeAuditListener( AuditListener listener )
+    {
+        this.auditListeners.remove( listener );
+    }
+
+    private void triggerAuditEvent( String user, String repositoryId, String resource, String action )
+    {
+        AuditEvent event = new AuditEvent( repositoryId, user, resource, action );
+        event.setRemoteIP( ServletActionContext.getRequest().getRemoteAddr() );
+
+        for ( AuditListener listener : auditListeners )
+        {
+            listener.auditEvent( event );
+        }
+    }
+}

Added: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/DeleteArtifactAction-validation.xml
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/DeleteArtifactAction-validation.xml?rev=746183&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/DeleteArtifactAction-validation.xml (added)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/DeleteArtifactAction-validation.xml Fri Feb 20 08:50:35 2009
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  ~ 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.
+  -->
+
+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
+    "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
+
+<validators>
+  <field name="groupId">
+    <field-validator type="requiredstring">
+      <message>You must enter a groupId.</message>
+    </field-validator>
+  </field>
+  <field name="artifactId">
+    <field-validator type="requiredstring">
+      <message>You must enter an artifactId.</message>
+    </field-validator>
+  </field>
+  <field name="version">
+    <field-validator type="requiredstring">
+      <message>You must enter a version.</message>
+    </field-validator>
+  </field>   
+</validators>
\ No newline at end of file

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml Fri Feb 20 08:50:35 2009
@@ -170,6 +170,12 @@
       <interceptor-ref name="fileUpload"/>
     </action>
 
+	<action name="deleteArtifact" class="deleteArtifactAction" method="input">      
+      <result name="input">/WEB-INF/jsp/deleteArtifact.jsp</result>
+      <result name="error">/WEB-INF/jsp/deleteArtifact.jsp</result>
+      <result name="success">/WEB-INF/jsp/deleteArtifact.jsp</result>
+    </action>
+
     <action name="checksumSearch" class="searchAction" method="findArtifact">
       <result name="input">/WEB-INF/jsp/findArtifact.jsp</result>
       <result name="results">/WEB-INF/jsp/results.jsp</result>

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/decorators/default.jsp
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/decorators/default.jsp?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/decorators/default.jsp (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/decorators/default.jsp Fri Feb 20 08:50:35 2009
@@ -80,7 +80,7 @@
       </li>
     </ul>
 
-    <redback:ifAnyAuthorized permissions="archiva-manage-users,archiva-access-reports,archiva-manage-configuration">
+    <redback:ifAnyAuthorized permissions="archiva-delete-artifact,archiva-manage-users,archiva-access-reports,archiva-manage-configuration">
       <h5>Manage</h5>
       <ul>
         <redback:ifAuthorized permission="archiva-access-reports">
@@ -111,6 +111,11 @@
             <my:currentWWUrl action="upload" namespace="/">Upload Artifact</my:currentWWUrl>
           </li>
         </redback:ifAuthorized>
+        <redback:ifAuthorized permission="archiva-delete-artifact">
+          <li class="none">
+            <my:currentWWUrl action="deleteArtifact" namespace="/">Delete Artifact</my:currentWWUrl>
+          </li>
+        </redback:ifAuthorized>
           <%-- TODO: future options here.
              * Repository Statistics.
              * Web Services Statistics.

Added: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/deleteArtifact.jsp
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/deleteArtifact.jsp?rev=746183&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/deleteArtifact.jsp (added)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/deleteArtifact.jsp Fri Feb 20 08:50:35 2009
@@ -0,0 +1,43 @@
+<%--
+  ~ 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.
+  --%>
+
+<%@ taglib prefix="s" uri="/struts-tags" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+<html>
+<head>
+  <title>Delete Artifact</title>
+  <s:head/>
+</head>
+
+<body>
+<h1>Delete Artifact</h1>
+
+  <s:actionerror/>
+  <s:actionmessage/>
+
+  <div id="contentArea">
+    <s:form action="deleteArtifact!doDelete" namespace="/" method="post" validate="true">    
+      <%@ include file="/WEB-INF/jsp/include/deleteArtifactForm.jspf" %>
+      <s:submit/>
+    </s:form>
+  </div>
+
+</body>
+</html>

Added: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/deleteArtifactForm.jspf
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/deleteArtifactForm.jspf?rev=746183&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/deleteArtifactForm.jspf (added)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/deleteArtifactForm.jspf Fri Feb 20 08:50:35 2009
@@ -0,0 +1,27 @@
+<%--
+  ~ 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.
+  --%>
+  
+<%@ taglib prefix="s" uri="/struts-tags" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+<s:textfield name="groupId" label="Group Id" size="50" required="true"/>
+<s:textfield name="artifactId" label="Artifact Id" size="50" required="true"/>
+<s:textfield name="version" label="Version" size="50" required="true"/>
+<s:select name="repositoryId" list="managedRepos" label="Repository Id"/>
+	
\ No newline at end of file

Modified: archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/rss/UserRepositoriesStub.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/rss/UserRepositoriesStub.java?rev=746183&r1=746182&r2=746183&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/rss/UserRepositoriesStub.java (original)
+++ archiva/trunk/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/rss/UserRepositoriesStub.java Fri Feb 20 08:50:35 2009
@@ -26,6 +26,8 @@
 import org.apache.maven.archiva.security.ArchivaSecurityException;
 import org.apache.maven.archiva.security.PrincipalNotFoundException;
 import org.apache.maven.archiva.security.UserRepositories;
+import org.codehaus.plexus.redback.rbac.RbacObjectNotFoundException;
+import org.codehaus.plexus.redback.rbac.RbacManagerException;
 
 /**
  * UserRepositories stub used for testing. 
@@ -58,5 +60,11 @@
         // TODO Auto-generated method stub
         return false;
     }
+    
+    public boolean isAuthorizedToDeleteArtifacts( String principal, String repoId )
+        throws RbacManagerException, RbacObjectNotFoundException
+    {
+        return false;
+    }
 
 }