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