]> source.dussan.org Git - archiva.git/blob
1abf8242dec09d054651bb57aab0b22475a587ce
[archiva.git] /
1 package org.apache.maven.archiva.webdav;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.servlet.http.HttpServletResponse;
32
33 import org.apache.commons.io.FileUtils;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.jackrabbit.webdav.DavException;
36 import org.apache.jackrabbit.webdav.DavResource;
37 import org.apache.jackrabbit.webdav.DavResourceFactory;
38 import org.apache.jackrabbit.webdav.DavResourceLocator;
39 import org.apache.jackrabbit.webdav.DavServletRequest;
40 import org.apache.jackrabbit.webdav.DavServletResponse;
41 import org.apache.jackrabbit.webdav.DavSession;
42 import org.apache.jackrabbit.webdav.lock.LockManager;
43 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
44 import org.apache.maven.archiva.common.utils.PathUtil;
45 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
46 import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration;
47 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
48 import org.apache.maven.archiva.model.ArtifactReference;
49 import org.apache.maven.archiva.model.VersionedReference;
50 import org.apache.maven.archiva.policies.ProxyDownloadException;
51 import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
52 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
53 import org.apache.maven.archiva.repository.RepositoryContentFactory;
54 import org.apache.maven.archiva.repository.RepositoryException;
55 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
56 import org.apache.maven.archiva.repository.audit.AuditEvent;
57 import org.apache.maven.archiva.repository.audit.AuditListener;
58 import org.apache.maven.archiva.repository.audit.Auditable;
59 import org.apache.maven.archiva.repository.content.RepositoryRequest;
60 import org.apache.maven.archiva.repository.layout.LayoutException;
61 import org.apache.maven.archiva.repository.metadata.MetadataTools;
62 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
63 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataMerge;
64 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
65 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
66 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
67 import org.apache.maven.archiva.security.ArchivaXworkUser;
68 import org.apache.maven.archiva.security.ServletAuthenticator;
69 import org.apache.maven.archiva.webdav.util.MimeTypes;
70 import org.apache.maven.archiva.webdav.util.RepositoryPathUtil;
71 import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
72 import org.apache.maven.model.DistributionManagement;
73 import org.apache.maven.model.Model;
74 import org.apache.maven.model.Relocation;
75 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
76 import org.codehaus.plexus.digest.ChecksumFile;
77 import org.codehaus.plexus.digest.Digester;
78 import org.codehaus.plexus.digest.DigesterException;
79 import org.codehaus.plexus.redback.authentication.AuthenticationException;
80 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
81 import org.codehaus.plexus.redback.authorization.AuthorizationException;
82 import org.codehaus.plexus.redback.authorization.UnauthorizedException;
83 import org.codehaus.plexus.redback.policy.AccountLockedException;
84 import org.codehaus.plexus.redback.policy.MustChangePasswordException;
85 import org.codehaus.plexus.redback.system.SecuritySession;
86 import org.codehaus.plexus.redback.system.SecuritySystemConstants;
87 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
88 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
91
92 import com.opensymphony.xwork2.ActionContext;
93
94 /**
95  * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
96  * @plexus.component role="org.apache.maven.archiva.webdav.ArchivaDavResourceFactory"
97  */
98 public class ArchivaDavResourceFactory
99     implements DavResourceFactory, Auditable
100 {   
101     private static final String PROXIED_SUFFIX = " (proxied)";
102
103     private static final String HTTP_PUT_METHOD = "PUT";
104
105     private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class );
106
107     /**
108      * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
109      */
110     private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
111
112     /**
113      * @plexus.requirement
114      */
115     private RepositoryContentFactory repositoryFactory;
116
117     /**
118      * @plexus.requirement
119      */
120     private RepositoryRequest repositoryRequest;
121
122     /**
123      * @plexus.requirement role-hint="default"
124      */
125     private RepositoryProxyConnectors connectors;
126
127     /**
128      * @plexus.requirement
129      */
130     private MetadataTools metadataTools;
131
132     /**
133      * @plexus.requirement
134      */
135     private MimeTypes mimeTypes;
136
137     /**
138      * @plexus.requirement
139      */
140     private ArchivaConfiguration archivaConfiguration;
141
142     /**
143      * @plexus.requirement
144      */
145     private ServletAuthenticator servletAuth;
146
147     /**
148      * @plexus.requirement role-hint="basic"
149      */
150     private HttpAuthenticator httpAuth;
151
152     /**
153      * Lock Manager - use simple implementation from JackRabbit
154      */
155     private final LockManager lockManager = new SimpleLockManager();
156
157     /** 
158      * @plexus.requirement 
159      */
160     private RepositoryContentConsumers consumers;
161     
162     /**
163      * @plexus.requirement
164      */
165     private ChecksumFile checksum;
166         
167     /**
168      * @plexus.requirement role-hint="sha1"
169      */
170     private Digester digestSha1;
171
172     /**
173      * @plexus.requirement role-hint="md5";
174      */
175     private Digester digestMd5;
176     
177     /**
178      * @plexus.requirement
179      */
180     private ArchivaXworkUser archivaXworkUser;
181         
182     public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request,
183                                        final DavServletResponse response )
184         throws DavException
185     {
186         checkLocatorIsInstanceOfRepositoryLocator( locator );
187         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
188         
189         RepositoryGroupConfiguration repoGroupConfig =
190             archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() );
191         List<String> repositories = new ArrayList<String>();
192
193         boolean isGet = WebdavMethodUtil.isReadMethod( request.getMethod() );
194         boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
195         
196         if ( repoGroupConfig != null )
197         {
198             if( WebdavMethodUtil.isWriteMethod( request.getMethod() ) )
199             {
200                 throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED,
201                                         "Write method not allowed for repository groups." );
202             }
203             repositories.addAll( repoGroupConfig.getRepositories() );
204
205             // handle browse requests for virtual repos
206             if ( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ).endsWith( "/" ) )
207             {
208                 return getResource( request, repositories, archivaLocator );
209             }
210         }
211         else
212         {
213             repositories.add( archivaLocator.getRepositoryId() );
214         }
215
216         //MRM-419 - Windows Webdav support. Should not 404 if there is no content.
217         if (StringUtils.isEmpty(archivaLocator.getRepositoryId()))
218         {
219             throw new DavException(HttpServletResponse.SC_NO_CONTENT);
220         }
221
222         List<DavResource> availableResources = new ArrayList<DavResource>();
223         List<String> resourcesInAbsolutePath = new ArrayList<String>();
224         DavException e = null;
225         
226         for ( String repositoryId : repositories )
227         {
228             ManagedRepositoryContent managedRepository = null;
229
230             try
231             {
232                 managedRepository = getManagedRepository( repositoryId );                
233             }
234             catch ( DavException de )
235             {
236                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" +
237                     repositoryId + ">" );
238             }
239             
240             DavResource resource = null;
241             
242             if ( !locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
243             {                
244                 if ( managedRepository != null )
245                 {
246                     try
247                     {
248                         if( isAuthorized( request, repositoryId ) )
249                         {   
250                             LogicalResource logicalResource =
251                                 new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
252
253                             if ( isGet )
254                             {
255                                 resource = doGet( managedRepository, request, archivaLocator, logicalResource );
256                             }
257
258                             if ( isPut )
259                             {
260                                 resource = doPut( managedRepository, request, archivaLocator, logicalResource );                                
261                             }
262                         }
263                     }
264                     catch ( DavException de ) 
265                     {                        
266                         e = de;
267                         continue;
268                     }
269
270                     if( resource == null )
271                     {
272                         e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
273                     }
274                     else
275                     {                           
276                         availableResources.add( resource );
277
278                         String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() );
279                         resourcesInAbsolutePath.add( managedRepository.getRepoRoot() + logicalResource );                        
280                     }
281                 }
282                 else
283                 {
284                     e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Repository does not exist" );
285                 }
286             }
287         }        
288         
289         if ( availableResources.isEmpty() )
290         {
291             throw e;
292         }
293         
294         String requestedResource = request.getRequestURI();
295         
296         // MRM-872 : merge all available metadata
297         // merge metadata only when requested via the repo group        
298         if ( ( repositoryRequest.isMetadata( requestedResource ) || ( requestedResource.endsWith( "metadata.xml.sha1" ) || requestedResource.endsWith( "metadata.xml.md5" ) ) ) &&
299             repoGroupConfig != null )
300         {   
301             // this should only be at the project level not version level!
302             if( isProjectReference( requestedResource ) )
303             {
304                 String artifactId = StringUtils.substringBeforeLast( requestedResource.replace( '\\', '/' ), "/" );
305                 artifactId = StringUtils.substringAfterLast( artifactId, "/" );
306                 
307                 ArchivaDavResource res = ( ArchivaDavResource ) availableResources.get( 0 );
308                 String filePath = StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ), "/" );                                
309                 filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml";
310                 
311                 // for MRM-872 handle checksums of the merged metadata files 
312                 if( repositoryRequest.isSupportFile( requestedResource ) )
313                 {
314                     File metadataChecksum = new File( filePath + "." 
315                               + StringUtils.substringAfterLast( requestedResource, "." ) );                    
316                     if( metadataChecksum.exists() )
317                     {
318                         LogicalResource logicalResource =
319                             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
320                                         
321                         ArchivaDavResource metadataChecksumResource =
322                             new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null,
323                                                     request.getRemoteAddr(), request.getDavSession(), archivaLocator, this,
324                                                     mimeTypes, auditListeners, consumers, archivaXworkUser );
325                         availableResources.add( 0, metadataChecksumResource );
326                     }
327                 }
328                 else
329                 {   // merge the metadata of all repos under group
330                     ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata();
331                     for ( String resourceAbsPath : resourcesInAbsolutePath )    
332                     {   
333                         try
334                         {   
335                             File metadataFile = new File( resourceAbsPath );
336                             ArchivaRepositoryMetadata repoMetadata = RepositoryMetadataReader.read( metadataFile );
337                             mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata );
338                         }
339                         catch ( RepositoryMetadataException r )
340                         {
341                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
342                                                     "Error occurred while reading metadata file." );
343                         }                
344                     }        
345                     
346                     try
347                     {   
348                         File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath );   
349                         
350                         LogicalResource logicalResource =
351                             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
352                                         
353                         ArchivaDavResource metadataResource =
354                             new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null,
355                                                     request.getRemoteAddr(), request.getDavSession(), archivaLocator, this,
356                                                     mimeTypes, auditListeners, consumers, archivaXworkUser );
357                         availableResources.add( 0, metadataResource );
358                     }
359                     catch ( RepositoryMetadataException r )
360                     {                
361                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
362                                                 "Error occurred while writing metadata file." );
363                     }
364                     catch ( IOException ie )
365                     {
366                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
367                             "Error occurred while generating checksum files." );
368                     }
369                     catch ( DigesterException de )
370                     {
371                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
372                             "Error occurred while generating checksum files." );
373                     }
374                 }
375             }
376         }
377                 
378         DavResource resource = availableResources.get( 0 );               
379         setHeaders(response, locator, resource );
380
381         // compatibility with MRM-440 to ensure browsing the repository works ok
382         if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) )
383         {
384             throw new BrowserRedirectException( resource.getHref() );
385         }
386         resource.addLockManager(lockManager);
387         return resource;
388     }
389
390     public DavResource createResource( final DavResourceLocator locator, final DavSession davSession )
391         throws DavException
392     {
393         checkLocatorIsInstanceOfRepositoryLocator( locator );
394         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
395
396         DavResource resource = null;
397         if ( !locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
398         {
399             ManagedRepositoryContent managedRepository = getManagedRepository( archivaLocator.getRepositoryId() );
400             String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() );
401             File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource );
402             resource =
403                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource,
404                                         managedRepository.getRepository(), davSession, archivaLocator, this, mimeTypes,
405                                         auditListeners, consumers, archivaXworkUser );
406         }
407         resource.addLockManager(lockManager);
408         return resource;
409     }
410
411     private DavResource doGet( ManagedRepositoryContent managedRepository, DavServletRequest request,
412                                ArchivaDavResourceLocator locator, LogicalResource logicalResource )
413         throws DavException
414     {
415         File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
416         
417         //MRM-893, dont send back a file when user intentionally wants a directory
418         if ( locator.getHref( false ).endsWith( "/" ) )
419         {
420             if ( ! resourceFile.isDirectory() )
421             {
422                 //force a resource not found 
423                 return null;
424             }
425         }
426
427         ArchivaDavResource resource =
428             new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
429                                     managedRepository.getRepository(), request.getRemoteAddr(),
430                                     request.getDavSession(), locator, this, mimeTypes, auditListeners, consumers, archivaXworkUser );
431
432         if ( !resource.isCollection() )
433         {
434             boolean previouslyExisted = resourceFile.exists();
435
436             // At this point the incoming request can either be in default or
437             // legacy layout format.
438             boolean fromProxy = fetchContentFromProxies( managedRepository, request, logicalResource );
439
440             try
441             {
442                 // Perform an adjustment of the resource to the managed
443                 // repository expected path.
444                 String localResourcePath =
445                     repositoryRequest.toNativePath( logicalResource.getPath(), managedRepository );
446                 resourceFile = new File( managedRepository.getRepoRoot(), localResourcePath );
447             }
448             catch ( LayoutException e )
449             {
450                 if ( previouslyExisted )
451                 {
452                     return resource;
453                 }
454                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
455             }
456
457             // Attempt to fetch the resource from any defined proxy.
458             if ( fromProxy )
459             {
460                 String repositoryId = locator.getRepositoryId();
461                 String event = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) + PROXIED_SUFFIX;
462                 triggerAuditEvent( request.getRemoteAddr(), repositoryId, logicalResource.getPath(), event );
463             }
464
465             if ( !resourceFile.exists() )
466             {
467                 resource = null;
468             }
469             else
470             {
471                 resource =
472                     new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
473                                             managedRepository.getRepository(), request.getRemoteAddr(),
474                                             request.getDavSession(), locator, this, mimeTypes, auditListeners,
475                                             consumers, archivaXworkUser );
476             }
477         }
478         return resource;
479     }
480
481     private DavResource doPut( ManagedRepositoryContent managedRepository, DavServletRequest request,
482                                ArchivaDavResourceLocator locator, LogicalResource logicalResource )
483         throws DavException
484     {
485         /*
486          * Create parent directories that don't exist when writing a file This actually makes this implementation not
487          * compliant to the WebDAV RFC - but we have enough knowledge about how the collection is being used to do this
488          * reasonably and some versions of Maven's WebDAV don't correctly create the collections themselves.
489          */
490
491         File rootDirectory = new File( managedRepository.getRepoRoot() );
492         File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile();
493         
494         if ( request.getMethod().equals(HTTP_PUT_METHOD) && !destDir.exists() )
495         {
496             destDir.mkdirs();
497             String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
498             triggerAuditEvent( request.getRemoteAddr(), logicalResource.getPath(), relPath, AuditEvent.CREATE_DIR );
499         }
500         
501         File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );        
502                 
503         return new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
504                                        managedRepository.getRepository(), request.getRemoteAddr(),
505                                        request.getDavSession(), locator, this, mimeTypes, auditListeners, consumers, archivaXworkUser );
506     }
507
508     private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request,
509                                              LogicalResource resource )
510         throws DavException
511     {
512         if ( repositoryRequest.isSupportFile( resource.getPath() ) )
513         {
514             File proxiedFile = connectors.fetchFromProxies( managedRepository, resource.getPath() );
515
516             return ( proxiedFile != null );
517         }
518
519         // Is it a Metadata resource?
520         if ( repositoryRequest.isDefault( resource.getPath() ) && repositoryRequest.isMetadata( resource.getPath() ) )
521         {
522             return connectors.fetchMetatadaFromProxies(managedRepository, resource.getPath()) != null;
523         }
524
525         // Not any of the above? Then it's gotta be an artifact reference.
526         try
527         {
528             // Get the artifact reference in a layout neutral way.
529             ArtifactReference artifact = repositoryRequest.toArtifactReference( resource.getPath() );
530
531             if ( artifact != null )
532             {
533                 applyServerSideRelocation( managedRepository, artifact );
534
535                 File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
536
537                 resource.setPath( managedRepository.toPath( artifact ) );
538
539                 return ( proxiedFile != null );
540             }
541         }
542         catch ( LayoutException e )
543         {
544             /* eat it */
545         }
546         catch ( ProxyDownloadException e )
547         {
548             log.error( e.getMessage(), e );
549             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to fetch artifact resource." );
550         }
551         return false;
552     }
553
554     /**
555      * A relocation capable client will request the POM prior to the artifact, and will then read meta-data and do
556      * client side relocation. A simplier client (like maven 1) will only request the artifact and not use the
557      * metadatas.
558      * <p>
559      * For such clients, archiva does server-side relocation by reading itself the &lt;relocation&gt; element in
560      * metadatas and serving the expected artifact.
561      */
562     protected void applyServerSideRelocation( ManagedRepositoryContent managedRepository, ArtifactReference artifact )
563         throws ProxyDownloadException
564     {
565         if ( "pom".equals( artifact.getType() ) )
566         {
567             return;
568         }
569
570         // Build the artifact POM reference
571         ArtifactReference pomReference = new ArtifactReference();
572         pomReference.setGroupId( artifact.getGroupId() );
573         pomReference.setArtifactId( artifact.getArtifactId() );
574         pomReference.setVersion( artifact.getVersion() );
575         pomReference.setType( "pom" );
576
577         // Get the artifact POM from proxied repositories if needed
578         connectors.fetchFromProxies( managedRepository, pomReference );
579
580         // Open and read the POM from the managed repo
581         File pom = managedRepository.toFile( pomReference );
582
583         if ( !pom.exists() )
584         {
585             return;
586         }
587
588         try
589         {
590             Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
591             DistributionManagement dist = model.getDistributionManagement();
592             if ( dist != null )
593             {
594                 Relocation relocation = dist.getRelocation();
595                 if ( relocation != null )
596                 {
597                     // artifact is relocated : update the repositoryPath
598                     if ( relocation.getGroupId() != null )
599                     {
600                         artifact.setGroupId( relocation.getGroupId() );
601                     }
602                     if ( relocation.getArtifactId() != null )
603                     {
604                         artifact.setArtifactId( relocation.getArtifactId() );
605                     }
606                     if ( relocation.getVersion() != null )
607                     {
608                         artifact.setVersion( relocation.getVersion() );
609                     }
610                 }
611             }
612         }
613         catch ( FileNotFoundException e )
614         {
615             // Artifact has no POM in repo : ignore
616         }
617         catch ( IOException e )
618         {
619             // Unable to read POM : ignore.
620         }
621         catch ( XmlPullParserException e )
622         {
623             // Invalid POM : ignore
624         }
625     }
626
627     // TODO: remove?
628     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action )
629     {
630         String activePrincipal = archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
631         AuditEvent event = new AuditEvent( repositoryId, activePrincipal, resource, action );
632         event.setRemoteIP( remoteIP );
633
634         for ( AuditListener listener : auditListeners )
635         {
636             listener.auditEvent( event );
637         }
638     }
639
640     public void addAuditListener( AuditListener listener )
641     {
642         this.auditListeners.add( listener );
643     }
644
645     public void clearAuditListeners()
646     {
647         this.auditListeners.clear();
648     }
649
650     public void removeAuditListener( AuditListener listener )
651     {
652         this.auditListeners.remove( listener );
653     }
654
655     private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource )
656     {
657         // [MRM-503] - Metadata file need Pragma:no-cache response
658         // header.
659         if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) )
660         {
661             response.addHeader( "Pragma", "no-cache" );
662             response.addHeader( "Cache-Control", "no-cache" );
663         }
664
665         //We need to specify this so connecting wagons can work correctly
666         response.addDateHeader("last-modified", resource.getModificationTime());
667
668         // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
669     }
670
671     private ManagedRepositoryContent getManagedRepository( String respositoryId )
672         throws DavException
673     {
674         if ( respositoryId != null )
675         {
676             try
677             {
678                 return repositoryFactory.getManagedRepositoryContent( respositoryId );
679             }
680             catch ( RepositoryNotFoundException e )
681             {
682                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
683             }
684             catch ( RepositoryException e )
685             {
686                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
687             }
688         }
689         return null;
690     }
691
692     private void checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator )
693         throws DavException
694     {
695         if ( !( locator instanceof RepositoryLocator ) )
696         {
697             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
698                                     "Locator does not implement RepositoryLocator" );
699         }
700     }
701
702     class LogicalResource
703     {
704         private String path;
705
706         public LogicalResource( String path )
707         {
708             this.path = path;
709         }
710
711         public String getPath()
712         {
713             return path;
714         }
715
716         public void setPath( String path )
717         {
718             this.path = path;
719         }
720     }
721
722     protected boolean isAuthorized( DavServletRequest request, String repositoryId )
723         throws DavException
724     {   
725         try
726         {     
727             AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
728             SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) );
729
730             return servletAuth.isAuthenticated( request, result ) &&
731                 servletAuth.isAuthorized( request, securitySession, repositoryId,
732                                           WebdavMethodUtil.isWriteMethod( request.getMethod() ) );
733         }
734         catch ( AuthenticationException e )
735         {            
736             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
737             
738             // safety check for MRM-911            
739             String guest = archivaXworkUser.getGuest();
740             try
741             {
742                 if( servletAuth.isAuthorized( guest, 
743                       ( ( ArchivaDavResourceLocator ) request.getRequestLocator() ).getRepositoryId(), isPut ) )
744                 {   
745                     return true;
746                 }
747             }
748             catch ( UnauthorizedException ae )
749             {
750                 throw new UnauthorizedDavException( repositoryId,
751                         "You are not authenticated and authorized to access any repository." );
752             }
753                         
754             throw new UnauthorizedDavException( repositoryId, "You are not authenticated" );
755         }
756         catch ( MustChangePasswordException e )
757         {
758             throw new UnauthorizedDavException( repositoryId, "You must change your password." );
759         }
760         catch ( AccountLockedException e )
761         {
762             throw new UnauthorizedDavException( repositoryId, "User account is locked." );
763         }
764         catch ( AuthorizationException e )
765         {
766             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
767                                     "Fatal Authorization Subsystem Error." );
768         }
769         catch ( UnauthorizedException e )
770         {
771             throw new UnauthorizedDavException( repositoryId, e.getMessage() );
772         }
773     }
774
775     private DavResource getResource( DavServletRequest request, List<String> repositories, ArchivaDavResourceLocator locator )
776         throws DavException
777     {
778         List<File> mergedRepositoryContents = new ArrayList<File>();
779         LogicalResource logicalResource =
780             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
781
782         // flow:
783         // if the current user logged in has permission to any of the repositories, allow user to
784         // browse the repo group but displaying only the repositories which the user has permission to access.
785         // otherwise, prompt for authentication.
786
787         // put the current session in the session map which will be passed to ArchivaXworkUser
788         Map<String, Object> sessionMap = new HashMap<String, Object>();
789         if( request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) != null )
790         {
791             sessionMap.put( SecuritySystemConstants.SECURITY_SESSION_KEY,
792                             request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) );
793         }
794
795         String activePrincipal = archivaXworkUser.getActivePrincipal( sessionMap );
796         boolean allow = isAllowedToContinue( request, repositories, activePrincipal );
797
798         if( allow )
799         {
800             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
801             
802             for( String repository : repositories )
803             {
804                 // for prompted authentication
805                 if( httpAuth.getSecuritySession( request.getSession( true ) ) != null )
806                 {
807                     try
808                     {
809                         if( isAuthorized( request, repository ) )
810                         {
811                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
812                         }
813                     }
814                     catch ( DavException e )
815                     {
816                         continue;
817                     }
818                 }
819                 else
820                 {
821                     // for the current user logged in
822                     try
823                     {
824                         if( servletAuth.isAuthorized( activePrincipal, repository, isPut ) )
825                         {
826                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
827                         }
828                     }
829                     catch ( UnauthorizedException e )
830                     {
831                         continue;
832                     }
833                 }
834             }
835         }
836         else
837         {
838             throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
839         }
840
841         ArchivaVirtualDavResource resource =
842             new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, this );
843
844         // compatibility with MRM-440 to ensure browsing the repository group works ok
845         if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) )
846         {
847             throw new BrowserRedirectException( resource.getHref() );
848         }
849
850         return resource;
851     }
852
853     private void getResource( ArchivaDavResourceLocator locator, List<File> mergedRepositoryContents,
854                               LogicalResource logicalResource, String repository )
855         throws DavException
856     {
857         ManagedRepositoryContent managedRepository = null;
858
859         try
860         {
861             managedRepository = getManagedRepository( repository );
862         }
863         catch ( DavException de )
864         {
865             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" +
866                 repository + ">" );
867         }
868
869         if ( !locator.getResourcePath().startsWith( ArchivaVirtualDavResource.HIDDEN_PATH_PREFIX ) )
870         {
871             if( managedRepository != null )
872             {
873                 File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
874                 if( resourceFile.exists() )
875                 {
876                     mergedRepositoryContents.add( resourceFile );
877                 }
878             }
879         }
880     }
881
882     /**
883      * Check if the current user is authorized to access any of the repos
884      *
885      * @param request
886      * @param repositories
887      * @param activePrincipal
888      * @return
889      */
890     private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal )
891     {
892         boolean allow = false;
893
894
895         // if securitySession != null, it means that the user was prompted for authentication
896         if( httpAuth.getSecuritySession( request.getSession() ) != null )
897         {
898             for( String repository : repositories )
899             {
900                 try
901                 {
902                     if( isAuthorized( request, repository ) )
903                     {
904                         allow = true;
905                         break;
906                     }
907                 }
908                 catch( DavException e )
909                 {
910                     continue;
911                 }
912             }
913         }
914         else
915         {
916             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
917             for( String repository : repositories )
918             {
919                 try
920                 {   
921                     if( servletAuth.isAuthorized( activePrincipal, repository, isPut ) )
922                     {
923                         allow = true;
924                         break;
925                     }
926                 }
927                 catch ( UnauthorizedException e )
928                 {
929                     continue;
930                 }
931             }
932         }
933
934         return allow;
935     }
936
937     private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename )
938         throws RepositoryMetadataException, DigesterException, IOException
939     {  
940         File outputFile = new File( outputFilename );        
941         if( outputFile.exists() )
942         {
943             FileUtils.deleteQuietly( outputFile );
944         }
945         
946         outputFile.getParentFile().mkdirs();
947         RepositoryMetadataWriter.write( mergedMetadata, outputFile );
948         
949         createChecksumFile( outputFilename, digestSha1 );
950         createChecksumFile( outputFilename, digestMd5 );
951         
952         return outputFile;
953     }
954     
955     private void createChecksumFile( String path, Digester digester )
956         throws DigesterException, IOException
957     {   
958         File checksumFile = new File( path + digester.getFilenameExtension() );        
959         if ( !checksumFile.exists() )
960         {
961             FileUtils.deleteQuietly( checksumFile );
962             checksum.createChecksum( new File( path ), digester );            
963         }
964         else if ( !checksumFile.isFile() )
965         {
966             log.error( "Checksum file is not a file." );
967         }
968     }
969     
970     private boolean isProjectReference( String requestedResource )
971     {  
972        try
973        {           
974            VersionedReference versionRef = metadataTools.toVersionedReference( requestedResource );           
975            return false;
976        }
977        catch ( RepositoryMetadataException re )
978        {
979            return true;
980        }
981     }
982     
983     public void setServletAuth( ServletAuthenticator servletAuth )
984     {
985         this.servletAuth = servletAuth;
986     }
987     
988     public void setHttpAuth( HttpAuthenticator httpAuth )
989     {
990         this.httpAuth = httpAuth;
991     }
992 }