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