]> source.dussan.org Git - archiva.git/blob
74f43e0ddb0373391ab2b15cc88553d2b6dd0032
[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             // MavenXpp3Reader leaves the file open, so we need to close it ourselves.
591             FileReader reader = new FileReader( pom );
592             Model model = new MavenXpp3Reader().read( reader );
593             reader.close();
594
595             DistributionManagement dist = model.getDistributionManagement();
596             if ( dist != null )
597             {
598                 Relocation relocation = dist.getRelocation();
599                 if ( relocation != null )
600                 {
601                     // artifact is relocated : update the repositoryPath
602                     if ( relocation.getGroupId() != null )
603                     {
604                         artifact.setGroupId( relocation.getGroupId() );
605                     }
606                     if ( relocation.getArtifactId() != null )
607                     {
608                         artifact.setArtifactId( relocation.getArtifactId() );
609                     }
610                     if ( relocation.getVersion() != null )
611                     {
612                         artifact.setVersion( relocation.getVersion() );
613                     }
614                 }
615             }
616         }
617         catch ( FileNotFoundException e )
618         {
619             // Artifact has no POM in repo : ignore
620         }
621         catch ( IOException e )
622         {
623             // Unable to read POM : ignore.
624         }
625         catch ( XmlPullParserException e )
626         {
627             // Invalid POM : ignore
628         }
629     }
630
631     // TODO: remove?
632     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action )
633     {
634         String activePrincipal = archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
635         AuditEvent event = new AuditEvent( repositoryId, activePrincipal, resource, action );
636         event.setRemoteIP( remoteIP );
637
638         for ( AuditListener listener : auditListeners )
639         {
640             listener.auditEvent( event );
641         }
642     }
643
644     public void addAuditListener( AuditListener listener )
645     {
646         this.auditListeners.add( listener );
647     }
648
649     public void clearAuditListeners()
650     {
651         this.auditListeners.clear();
652     }
653
654     public void removeAuditListener( AuditListener listener )
655     {
656         this.auditListeners.remove( listener );
657     }
658
659     private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource )
660     {
661         // [MRM-503] - Metadata file need Pragma:no-cache response
662         // header.
663         if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) )
664         {
665             response.addHeader( "Pragma", "no-cache" );
666             response.addHeader( "Cache-Control", "no-cache" );
667         }
668
669         //We need to specify this so connecting wagons can work correctly
670         response.addDateHeader("last-modified", resource.getModificationTime());
671
672         // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
673     }
674
675     private ManagedRepositoryContent getManagedRepository( String respositoryId )
676         throws DavException
677     {
678         if ( respositoryId != null )
679         {
680             try
681             {
682                 return repositoryFactory.getManagedRepositoryContent( respositoryId );
683             }
684             catch ( RepositoryNotFoundException e )
685             {
686                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
687             }
688             catch ( RepositoryException e )
689             {
690                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
691             }
692         }
693         return null;
694     }
695
696     private void checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator )
697         throws DavException
698     {
699         if ( !( locator instanceof RepositoryLocator ) )
700         {
701             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
702                                     "Locator does not implement RepositoryLocator" );
703         }
704     }
705
706     class LogicalResource
707     {
708         private String path;
709
710         public LogicalResource( String path )
711         {
712             this.path = path;
713         }
714
715         public String getPath()
716         {
717             return path;
718         }
719
720         public void setPath( String path )
721         {
722             this.path = path;
723         }
724     }
725
726     protected boolean isAuthorized( DavServletRequest request, String repositoryId )
727         throws DavException
728     {   
729         try
730         {     
731             AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
732             SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) );
733
734             return servletAuth.isAuthenticated( request, result ) &&
735                 servletAuth.isAuthorized( request, securitySession, repositoryId,
736                                           WebdavMethodUtil.isWriteMethod( request.getMethod() ) );
737         }
738         catch ( AuthenticationException e )
739         {            
740             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
741             
742             // safety check for MRM-911            
743             String guest = archivaXworkUser.getGuest();
744             try
745             {
746                 if( servletAuth.isAuthorized( guest, 
747                       ( ( ArchivaDavResourceLocator ) request.getRequestLocator() ).getRepositoryId(), isPut ) )
748                 {   
749                     return true;
750                 }
751             }
752             catch ( UnauthorizedException ae )
753             {
754                 throw new UnauthorizedDavException( repositoryId,
755                         "You are not authenticated and authorized to access any repository." );
756             }
757                         
758             throw new UnauthorizedDavException( repositoryId, "You are not authenticated" );
759         }
760         catch ( MustChangePasswordException e )
761         {
762             throw new UnauthorizedDavException( repositoryId, "You must change your password." );
763         }
764         catch ( AccountLockedException e )
765         {
766             throw new UnauthorizedDavException( repositoryId, "User account is locked." );
767         }
768         catch ( AuthorizationException e )
769         {
770             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
771                                     "Fatal Authorization Subsystem Error." );
772         }
773         catch ( UnauthorizedException e )
774         {
775             throw new UnauthorizedDavException( repositoryId, e.getMessage() );
776         }
777     }
778
779     private DavResource getResource( DavServletRequest request, List<String> repositories, ArchivaDavResourceLocator locator )
780         throws DavException
781     {
782         List<File> mergedRepositoryContents = new ArrayList<File>();
783         LogicalResource logicalResource =
784             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
785
786         // flow:
787         // if the current user logged in has permission to any of the repositories, allow user to
788         // browse the repo group but displaying only the repositories which the user has permission to access.
789         // otherwise, prompt for authentication.
790
791         // put the current session in the session map which will be passed to ArchivaXworkUser
792         Map<String, Object> sessionMap = new HashMap<String, Object>();
793         if( request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) != null )
794         {
795             sessionMap.put( SecuritySystemConstants.SECURITY_SESSION_KEY,
796                             request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) );
797         }
798
799         String activePrincipal = archivaXworkUser.getActivePrincipal( sessionMap );
800         boolean allow = isAllowedToContinue( request, repositories, activePrincipal );
801
802         if( allow )
803         {
804             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
805             
806             for( String repository : repositories )
807             {
808                 // for prompted authentication
809                 if( httpAuth.getSecuritySession( request.getSession( true ) ) != null )
810                 {
811                     try
812                     {
813                         if( isAuthorized( request, repository ) )
814                         {
815                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
816                         }
817                     }
818                     catch ( DavException e )
819                     {
820                         continue;
821                     }
822                 }
823                 else
824                 {
825                     // for the current user logged in
826                     try
827                     {
828                         if( servletAuth.isAuthorized( activePrincipal, repository, isPut ) )
829                         {
830                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
831                         }
832                     }
833                     catch ( UnauthorizedException e )
834                     {
835                         continue;
836                     }
837                 }
838             }
839         }
840         else
841         {
842             throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
843         }
844
845         ArchivaVirtualDavResource resource =
846             new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, this );
847
848         // compatibility with MRM-440 to ensure browsing the repository group works ok
849         if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) )
850         {
851             throw new BrowserRedirectException( resource.getHref() );
852         }
853
854         return resource;
855     }
856
857     private void getResource( ArchivaDavResourceLocator locator, List<File> mergedRepositoryContents,
858                               LogicalResource logicalResource, String repository )
859         throws DavException
860     {
861         ManagedRepositoryContent managedRepository = null;
862
863         try
864         {
865             managedRepository = getManagedRepository( repository );
866         }
867         catch ( DavException de )
868         {
869             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" +
870                 repository + ">" );
871         }
872
873         if ( !locator.getResourcePath().startsWith( ArchivaVirtualDavResource.HIDDEN_PATH_PREFIX ) )
874         {
875             if( managedRepository != null )
876             {
877                 File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
878                 if( resourceFile.exists() )
879                 {
880                     mergedRepositoryContents.add( resourceFile );
881                 }
882             }
883         }
884     }
885
886     /**
887      * Check if the current user is authorized to access any of the repos
888      *
889      * @param request
890      * @param repositories
891      * @param activePrincipal
892      * @return
893      */
894     private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal )
895     {
896         boolean allow = false;
897
898
899         // if securitySession != null, it means that the user was prompted for authentication
900         if( httpAuth.getSecuritySession( request.getSession() ) != null )
901         {
902             for( String repository : repositories )
903             {
904                 try
905                 {
906                     if( isAuthorized( request, repository ) )
907                     {
908                         allow = true;
909                         break;
910                     }
911                 }
912                 catch( DavException e )
913                 {
914                     continue;
915                 }
916             }
917         }
918         else
919         {
920             boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
921             for( String repository : repositories )
922             {
923                 try
924                 {   
925                     if( servletAuth.isAuthorized( activePrincipal, repository, isPut ) )
926                     {
927                         allow = true;
928                         break;
929                     }
930                 }
931                 catch ( UnauthorizedException e )
932                 {
933                     continue;
934                 }
935             }
936         }
937
938         return allow;
939     }
940
941     private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename )
942         throws RepositoryMetadataException, DigesterException, IOException
943     {  
944         File outputFile = new File( outputFilename );        
945         if( outputFile.exists() )
946         {
947             FileUtils.deleteQuietly( outputFile );
948         }
949         
950         outputFile.getParentFile().mkdirs();
951         RepositoryMetadataWriter.write( mergedMetadata, outputFile );
952         
953         createChecksumFile( outputFilename, digestSha1 );
954         createChecksumFile( outputFilename, digestMd5 );
955         
956         return outputFile;
957     }
958     
959     private void createChecksumFile( String path, Digester digester )
960         throws DigesterException, IOException
961     {   
962         File checksumFile = new File( path + digester.getFilenameExtension() );        
963         if ( !checksumFile.exists() )
964         {
965             FileUtils.deleteQuietly( checksumFile );
966             checksum.createChecksum( new File( path ), digester );            
967         }
968         else if ( !checksumFile.isFile() )
969         {
970             log.error( "Checksum file is not a file." );
971         }
972     }
973     
974     private boolean isProjectReference( String requestedResource )
975     {  
976        try
977        {           
978            VersionedReference versionRef = metadataTools.toVersionedReference( requestedResource );           
979            return false;
980        }
981        catch ( RepositoryMetadataException re )
982        {
983            return true;
984        }
985     }
986     
987     public void setServletAuth( ServletAuthenticator servletAuth )
988     {
989         this.servletAuth = servletAuth;
990     }
991     
992     public void setHttpAuth( HttpAuthenticator httpAuth )
993     {
994         this.httpAuth = httpAuth;
995     }
996 }