]> source.dussan.org Git - archiva.git/blob
6132dfe01a782630677433835bcef0b1a97be79e
[archiva.git] /
1 package org.apache.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 org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.RemoteRepository;
25 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
26 import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
27 import org.apache.archiva.audit.AuditEvent;
28 import org.apache.archiva.audit.AuditListener;
29 import org.apache.archiva.audit.Auditable;
30 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
31 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
32 import org.apache.archiva.common.utils.PathUtil;
33 import org.apache.archiva.common.utils.VersionUtil;
34 import org.apache.archiva.configuration.ArchivaConfiguration;
35 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
36 import org.apache.archiva.indexer.merger.IndexMerger;
37 import org.apache.archiva.indexer.merger.IndexMergerException;
38 import org.apache.archiva.indexer.merger.IndexMergerRequest;
39 import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
40 import org.apache.archiva.indexer.search.RepositorySearch;
41 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
42 import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
43 import org.apache.archiva.model.ArchivaRepositoryMetadata;
44 import org.apache.archiva.model.ArtifactReference;
45 import org.apache.archiva.policies.ProxyDownloadException;
46 import org.apache.archiva.proxy.model.RepositoryProxyConnectors;
47 import org.apache.archiva.redback.authentication.AuthenticationException;
48 import org.apache.archiva.redback.authentication.AuthenticationResult;
49 import org.apache.archiva.redback.authorization.AuthorizationException;
50 import org.apache.archiva.redback.authorization.UnauthorizedException;
51 import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
52 import org.apache.archiva.redback.policy.AccountLockedException;
53 import org.apache.archiva.redback.policy.MustChangePasswordException;
54 import org.apache.archiva.redback.system.SecuritySession;
55 import org.apache.archiva.redback.users.User;
56 import org.apache.archiva.redback.users.UserManager;
57 import org.apache.archiva.repository.ManagedRepositoryContent;
58 import org.apache.archiva.repository.RepositoryContentFactory;
59 import org.apache.archiva.repository.RepositoryException;
60 import org.apache.archiva.repository.RepositoryNotFoundException;
61 import org.apache.archiva.repository.content.legacy.LegacyPathParser;
62 import org.apache.archiva.repository.content.maven2.RepositoryRequest;
63 import org.apache.archiva.repository.layout.LayoutException;
64 import org.apache.archiva.repository.metadata.MetadataTools;
65 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
66 import org.apache.archiva.repository.metadata.RepositoryMetadataMerge;
67 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
68 import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler;
69 import org.apache.archiva.security.ServletAuthenticator;
70 import org.apache.archiva.webdav.util.MimeTypes;
71 import org.apache.archiva.webdav.util.RepositoryPathUtil;
72 import org.apache.archiva.webdav.util.TemporaryGroupIndexSessionCleaner;
73 import org.apache.archiva.webdav.util.WebdavMethodUtil;
74 import org.apache.archiva.xml.XMLException;
75 import org.apache.commons.io.FileUtils;
76 import org.apache.commons.io.FilenameUtils;
77 import org.apache.commons.lang.StringUtils;
78 import org.apache.commons.lang.SystemUtils;
79 import org.apache.jackrabbit.webdav.DavException;
80 import org.apache.jackrabbit.webdav.DavResource;
81 import org.apache.jackrabbit.webdav.DavResourceFactory;
82 import org.apache.jackrabbit.webdav.DavResourceLocator;
83 import org.apache.jackrabbit.webdav.DavServletRequest;
84 import org.apache.jackrabbit.webdav.DavServletResponse;
85 import org.apache.jackrabbit.webdav.DavSession;
86 import org.apache.jackrabbit.webdav.lock.LockManager;
87 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
88 import org.apache.maven.index.context.IndexingContext;
89 import org.codehaus.plexus.digest.ChecksumFile;
90 import org.codehaus.plexus.digest.Digester;
91 import org.codehaus.plexus.digest.DigesterException;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
94 import org.slf4j.MarkerFactory;
95 import org.springframework.context.ApplicationContext;
96 import org.springframework.stereotype.Service;
97
98 import javax.annotation.PostConstruct;
99 import javax.inject.Inject;
100 import javax.inject.Named;
101 import javax.servlet.http.HttpServletResponse;
102 import javax.servlet.http.HttpSession;
103 import java.io.File;
104 import java.io.IOException;
105 import java.util.ArrayList;
106 import java.util.Date;
107 import java.util.HashMap;
108 import java.util.HashSet;
109 import java.util.List;
110 import java.util.Map;
111 import java.util.Set;
112
113 /**
114  *
115  */
116 @Service( "davResourceFactory#archiva" )
117 public class ArchivaDavResourceFactory
118     implements DavResourceFactory, Auditable
119 {
120     private static final String PROXIED_SUFFIX = " (proxied)";
121
122     private static final String HTTP_PUT_METHOD = "PUT";
123
124     private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class );
125
126     @Inject
127     private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
128
129     @Inject
130     private RepositoryContentFactory repositoryFactory;
131
132     private RepositoryRequest repositoryRequest;
133
134     @Inject
135     @Named( value = "repositoryProxyConnectors#default" )
136     private RepositoryProxyConnectors connectors;
137
138     @Inject
139     private MetadataTools metadataTools;
140
141     @Inject
142     private MimeTypes mimeTypes;
143
144     private ArchivaConfiguration archivaConfiguration;
145
146     @Inject
147     private ServletAuthenticator servletAuth;
148
149     @Inject
150     @Named( value = "httpAuthenticator#basic" )
151     private HttpAuthenticator httpAuth;
152
153     @Inject
154     private RemoteRepositoryAdmin remoteRepositoryAdmin;
155
156     @Inject
157     private ManagedRepositoryAdmin managedRepositoryAdmin;
158
159     @Inject
160     private IndexMerger indexMerger;
161
162     @Inject
163     private RepositorySearch repositorySearch;
164
165     /**
166      * Lock Manager - use simple implementation from JackRabbit
167      */
168     private final LockManager lockManager = new SimpleLockManager();
169
170     private ChecksumFile checksum;
171
172     private Digester digestSha1;
173
174     private Digester digestMd5;
175
176     @Inject
177     @Named( value = "archivaTaskScheduler#repository" )
178     private RepositoryArchivaTaskScheduler scheduler;
179
180     private ApplicationContext applicationContext;
181
182     @Inject
183     public ArchivaDavResourceFactory( ApplicationContext applicationContext, PlexusSisuBridge plexusSisuBridge,
184                                       ArchivaConfiguration archivaConfiguration )
185         throws PlexusSisuBridgeException
186     {
187         this.archivaConfiguration = archivaConfiguration;
188         this.applicationContext = applicationContext;
189         this.checksum = plexusSisuBridge.lookup( ChecksumFile.class );
190
191         this.digestMd5 = plexusSisuBridge.lookup( Digester.class, "md5" );
192         this.digestSha1 = plexusSisuBridge.lookup( Digester.class, "sha1" );
193
194         // TODO remove this hard dependency on maven !!
195         repositoryRequest = new RepositoryRequest( new LegacyPathParser( archivaConfiguration ) );
196     }
197
198     @PostConstruct
199     public void initialize()
200     {
201         // no op
202     }
203
204     public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request,
205                                        final DavServletResponse response )
206         throws DavException
207     {
208         ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator );
209
210         RepositoryGroupConfiguration repoGroupConfig =
211             archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() );
212
213         String activePrincipal = getActivePrincipal( request );
214
215         List<String> resourcesInAbsolutePath = new ArrayList<String>();
216
217         boolean readMethod = WebdavMethodUtil.isReadMethod( request.getMethod() );
218         DavResource resource;
219         if ( repoGroupConfig != null )
220         {
221             if ( !readMethod )
222             {
223                 throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED,
224                                         "Write method not allowed for repository groups." );
225             }
226
227             log.debug( "Repository group '{}' accessed by '{}", repoGroupConfig.getId(), activePrincipal );
228
229             // handle browse requests for virtual repos
230             if ( getLogicalResource( archivaLocator, null, true ).endsWith( "/" ) )
231             {
232                 try
233                 {
234                     return getResourceFromGroup( request, repoGroupConfig.getRepositories(), archivaLocator,
235                                                  repoGroupConfig );
236                 }
237                 catch ( RepositoryAdminException e )
238                 {
239                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
240                 }
241             }
242             else
243             {
244                 // make a copy to avoid potential concurrent modifications (eg. by configuration)
245                 // TODO: ultimately, locking might be more efficient than copying in this fashion since updates are
246                 //  infrequent
247                 List<String> repositories = new ArrayList<String>( repoGroupConfig.getRepositories() );
248                 resource = processRepositoryGroup( request, archivaLocator, repositories, activePrincipal,
249                                                    resourcesInAbsolutePath, repoGroupConfig );
250             }
251         }
252         else
253         {
254
255             try
256             {
257                 RemoteRepository remoteRepository =
258                     remoteRepositoryAdmin.getRemoteRepository( archivaLocator.getRepositoryId() );
259
260                 if ( remoteRepository != null )
261                 {
262                     String logicalResource = getLogicalResource( archivaLocator, null, false );
263                     IndexingContext indexingContext = remoteRepositoryAdmin.createIndexContext( remoteRepository );
264                     File resourceFile = StringUtils.equals( logicalResource, "/" )
265                         ? new File( indexingContext.getIndexDirectoryFile().getParent() )
266                         : new File( indexingContext.getIndexDirectoryFile().getParent(), logicalResource );
267                     resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), locator.getResourcePath(), null,
268                                                        request.getRemoteAddr(), activePrincipal,
269                                                        request.getDavSession(), archivaLocator, this, mimeTypes,
270                                                        auditListeners, scheduler );
271                     setHeaders( response, locator, resource );
272                     return resource;
273                 }
274             }
275             catch ( RepositoryAdminException e )
276             {
277                 log.debug( "RepositoryException remote repository with d'{}' not found, msg: {}",
278                            archivaLocator.getRepositoryId(), e.getMessage() );
279             }
280
281             ManagedRepositoryContent managedRepositoryContent = null;
282
283             try
284             {
285                 managedRepositoryContent =
286                     repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() );
287             }
288             catch ( RepositoryNotFoundException e )
289             {
290                 throw new DavException( HttpServletResponse.SC_NOT_FOUND,
291                                         "Invalid repository: " + archivaLocator.getRepositoryId() );
292             }
293             catch ( RepositoryException e )
294             {
295                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
296             }
297
298             log.debug( "Managed repository '{}' accessed by '{}'", managedRepositoryContent.getId(), activePrincipal );
299
300             try
301             {
302                 resource = processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent,
303                                               managedRepositoryAdmin.getManagedRepository(
304                                                   archivaLocator.getRepositoryId() ) );
305
306                 String logicalResource = getLogicalResource( archivaLocator, null, false );
307                 resourcesInAbsolutePath.add(
308                     new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() );
309
310             }
311             catch ( RepositoryAdminException e )
312             {
313                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
314             }
315         }
316
317         String requestedResource = request.getRequestURI();
318
319         // MRM-872 : merge all available metadata
320         // merge metadata only when requested via the repo group
321         if ( ( repositoryRequest.isMetadata( requestedResource ) || repositoryRequest.isMetadataSupportFile(
322             requestedResource ) ) && repoGroupConfig != null )
323         {
324             // this should only be at the project level not version level!
325             if ( isProjectReference( requestedResource ) )
326             {
327
328                 ArchivaDavResource res = (ArchivaDavResource) resource;
329                 String filePath =
330                     StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ),
331                                                      "/" );
332                 filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml";
333
334                 // for MRM-872 handle checksums of the merged metadata files
335                 if ( repositoryRequest.isSupportFile( requestedResource ) )
336                 {
337                     File metadataChecksum =
338                         new File( filePath + "." + StringUtils.substringAfterLast( requestedResource, "." ) );
339                     if ( metadataChecksum.exists() )
340                     {
341                         LogicalResource logicalResource =
342                             new LogicalResource (getLogicalResource( archivaLocator, null, false ) );
343
344                         resource =
345                             new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null,
346                                                     request.getRemoteAddr(), activePrincipal, request.getDavSession(),
347                                                     archivaLocator, this, mimeTypes, auditListeners, scheduler );
348                     }
349                 }
350                 else
351                 {
352                     if ( resourcesInAbsolutePath != null && resourcesInAbsolutePath.size() > 1 )
353                     {
354                         // merge the metadata of all repos under group
355                         ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata();
356                         for ( String resourceAbsPath : resourcesInAbsolutePath )
357                         {
358                             try
359                             {
360                                 File metadataFile = new File( resourceAbsPath );
361                                 ArchivaRepositoryMetadata repoMetadata = MavenMetadataReader.read( metadataFile );
362                                 mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata );
363                             }
364                             catch ( XMLException e )
365                             {
366                                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
367                                                         "Error occurred while reading metadata file." );
368                             }
369                             catch ( RepositoryMetadataException r )
370                             {
371                                 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
372                                                         "Error occurred while merging metadata file." );
373                             }
374                         }
375
376                         try
377                         {
378                             File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath );
379
380                             LogicalResource logicalResource = new LogicalResource( getLogicalResource( archivaLocator, null, false ) );
381
382                             resource =
383                                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null,
384                                                         request.getRemoteAddr(), activePrincipal,
385                                                         request.getDavSession(), archivaLocator, this, mimeTypes,
386                                                         auditListeners, scheduler );
387                         }
388                         catch ( RepositoryMetadataException r )
389                         {
390                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
391                                                     "Error occurred while writing metadata file." );
392                         }
393                         catch ( IOException ie )
394                         {
395                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
396                                                     "Error occurred while generating checksum files." );
397                         }
398                         catch ( DigesterException de )
399                         {
400                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
401                                                     "Error occurred while generating checksum files." + de.getMessage() );
402                         }
403                     }
404                 }
405             }
406         }
407
408         setHeaders( response, locator, resource );
409
410         // compatibility with MRM-440 to ensure browsing the repository works ok
411         if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) )
412         {
413             throw new BrowserRedirectException( resource.getHref() );
414         }
415         resource.addLockManager( lockManager );
416         return resource;
417     }
418
419     private DavResource processRepositoryGroup( final DavServletRequest request,
420                                                 ArchivaDavResourceLocator archivaLocator, List<String> repositories,
421                                                 String activePrincipal, List<String> resourcesInAbsolutePath,
422                                                 RepositoryGroupConfiguration repoGroupConfig )
423         throws DavException
424     {
425         DavResource resource = null;
426         List<DavException> storedExceptions = new ArrayList<DavException>();
427
428         String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" );
429
430         String rootPath = StringUtils.substringBeforeLast( pathInfo, "/" );
431
432         if ( StringUtils.endsWith( rootPath, repoGroupConfig.getMergedIndexPath() ) )
433         {
434             // we are in the case of index file request
435             String requestedFileName = StringUtils.substringAfterLast( pathInfo, "/" );
436             File temporaryIndexDirectory =
437                 buildMergedIndexDirectory( repositories, activePrincipal, request, repoGroupConfig );
438
439             File resourceFile = new File( temporaryIndexDirectory, requestedFileName );
440             resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), requestedFileName, null,
441                                                request.getRemoteAddr(), activePrincipal, request.getDavSession(),
442                                                archivaLocator, this, mimeTypes, auditListeners, scheduler );
443
444         }
445         else
446         {
447             for ( String repositoryId : repositories )
448             {
449                 ManagedRepositoryContent managedRepositoryContent;
450                 try
451                 {
452                     managedRepositoryContent = repositoryFactory.getManagedRepositoryContent( repositoryId );
453                 }
454                 catch ( RepositoryNotFoundException e )
455                 {
456                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
457                 }
458                 catch ( RepositoryException e )
459                 {
460                     throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
461                 }
462
463                 try
464                 {
465                     ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repositoryId );
466                     DavResource updatedResource =
467                         processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent,
468                                            managedRepository );
469                     if ( resource == null )
470                     {
471                         resource = updatedResource;
472                     }
473
474                     String logicalResource = getLogicalResource( archivaLocator, null, false );
475                     if ( logicalResource.endsWith( "/" ) )
476                     {
477                         logicalResource = logicalResource.substring( 1 );
478                     }
479                     resourcesInAbsolutePath.add(
480                         new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() );
481                 }
482                 catch ( DavException e )
483                 {
484                     storedExceptions.add( e );
485                 }
486                 catch ( RepositoryAdminException e )
487                 {
488                     storedExceptions.add( new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ) );
489                 }
490             }
491         }
492         if ( resource == null )
493         {
494             if ( !storedExceptions.isEmpty() )
495             {
496                 // MRM-1232
497                 for ( DavException e : storedExceptions )
498                 {
499                     if ( 401 == e.getErrorCode() )
500                     {
501                         throw e;
502                     }
503                 }
504
505                 throw new DavException( HttpServletResponse.SC_NOT_FOUND );
506             }
507             else
508             {
509                 throw new DavException( HttpServletResponse.SC_NOT_FOUND );
510             }
511         }
512         return resource;
513     }
514
515     private String getLogicalResource( ArchivaDavResourceLocator archivaLocator, ManagedRepository managedRepository,
516                                        boolean useOrigResourcePath )
517     {
518         // FIXME remove this hack
519         // but currently managedRepository can be null in case of group
520         String layout = managedRepository == null ? new ManagedRepository( ).getLayout() : managedRepository.getLayout();
521         RepositoryStorage repositoryStorage =
522             this.applicationContext.getBean( "repositoryStorage#" + layout, RepositoryStorage.class );
523         String path = repositoryStorage.getFilePath(
524             useOrigResourcePath ? archivaLocator.getOrigResourcePath() : archivaLocator.getResourcePath(), managedRepository );
525         log.debug( "found path {} for resourcePath: '{}' with managedRepo '{}' and layout '{}'", path,
526                    archivaLocator.getResourcePath(), managedRepository == null ? "null" : managedRepository.getId(), layout );
527         return path;
528     }
529
530     private String evaluatePathWithVersion( ArchivaDavResourceLocator archivaLocator, ManagedRepositoryContent managedRepositoryContent )
531         throws DavException
532     {
533         String layout = managedRepositoryContent.getRepository() == null ? new ManagedRepository( ).getLayout() : managedRepositoryContent.getRepository().getLayout();
534         RepositoryStorage repositoryStorage =
535             this.applicationContext.getBean( "repositoryStorage#" + layout, RepositoryStorage.class );
536         try
537         {
538             return repositoryStorage.getFilePathWithVersion( archivaLocator.getResourcePath(), managedRepositoryContent );
539         }
540         catch ( LayoutException e )
541         {
542             log.error( e.getMessage(), e );
543             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
544         }
545         catch ( XMLException e )
546         {
547             log.error( e.getMessage(), e );
548             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
549         }
550     }
551
552     private DavResource processRepository( final DavServletRequest request, ArchivaDavResourceLocator archivaLocator,
553                                            String activePrincipal, ManagedRepositoryContent managedRepositoryContent,
554                                            ManagedRepository managedRepository )
555         throws DavException
556     {
557         DavResource resource = null;
558         if ( isAuthorized( request, managedRepositoryContent.getId() ) )
559         {
560             // Maven Centric part ask evaluation if -SNAPSHOT
561             String path = evaluatePathWithVersion(archivaLocator, managedRepositoryContent);
562             if ( path.startsWith( "/" ) )
563             {
564                 path = path.substring( 1 );
565             }
566             LogicalResource logicalResource = new LogicalResource( path );
567             File resourceFile = new File( managedRepositoryContent.getRepoRoot(), path );
568             resource =
569                 new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepositoryContent.getRepository(),
570                                         request.getRemoteAddr(), activePrincipal, request.getDavSession(),
571                                         archivaLocator, this, mimeTypes, auditListeners, scheduler );
572
573             if ( WebdavMethodUtil.isReadMethod( request.getMethod() ) )
574             {
575                 if ( archivaLocator.getHref( false ).endsWith( "/" ) && !resourceFile.isDirectory() )
576                 {
577                     // force a resource not found
578                     throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
579                 }
580                 else
581                 {
582                     if ( !resource.isCollection() )
583                     {
584                         boolean previouslyExisted = resourceFile.exists();
585
586                         // Attempt to fetch the resource from any defined proxy.
587                         boolean fromProxy =
588                             fetchContentFromProxies( managedRepositoryContent, request, logicalResource );
589
590                         // At this point the incoming request can either be in default or
591                         // legacy layout format.
592                         try
593                         {
594                             // Perform an adjustment of the resource to the managed
595                             // repository expected path.
596                             String localResourcePath =
597                                 repositoryRequest.toNativePath( logicalResource.getPath(), managedRepositoryContent );
598                             resourceFile = new File( managedRepositoryContent.getRepoRoot(), localResourcePath );
599                             resource =
600                                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
601                                                         managedRepositoryContent.getRepository(),
602                                                         request.getRemoteAddr(), activePrincipal,
603                                                         request.getDavSession(), archivaLocator, this, mimeTypes,
604                                                         auditListeners, scheduler );
605                         }
606                         catch ( LayoutException e )
607                         {
608                             if ( !resourceFile.exists() )
609                             {
610                                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
611                             }
612                         }
613
614                         if ( fromProxy )
615                         {
616                             String event = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE )
617                                 + PROXIED_SUFFIX;
618
619                             log.debug( "Proxied artifact '{}' in repository '{}' (current user '{}')",
620                                        resourceFile.getName(), managedRepositoryContent.getId(), activePrincipal );
621
622                             triggerAuditEvent( request.getRemoteAddr(), archivaLocator.getRepositoryId(),
623                                                logicalResource.getPath(), event, activePrincipal );
624                         }
625
626                         if ( !resourceFile.exists() )
627                         {
628                             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
629                         }
630                     }
631                 }
632             }
633
634             if ( request.getMethod().equals( HTTP_PUT_METHOD ) )
635             {
636                 String resourcePath = logicalResource.getPath();
637
638                 // check if target repo is enabled for releases
639                 // we suppose that release-artifacts can be deployed only to repos enabled for releases
640                 if ( managedRepositoryContent.getRepository().isReleases() && !repositoryRequest.isMetadata(
641                     resourcePath ) && !repositoryRequest.isSupportFile( resourcePath ) )
642                 {
643                     ArtifactReference artifact = null;
644                     try
645                     {
646                         artifact = managedRepositoryContent.toArtifactReference( resourcePath );
647
648                         if ( !VersionUtil.isSnapshot( artifact.getVersion() ) )
649                         {
650                             // check if artifact already exists and if artifact re-deployment to the repository is allowed
651                             if ( managedRepositoryContent.hasContent( artifact )
652                                 && managedRepositoryContent.getRepository().isBlockRedeployments() )
653                             {
654                                 log.warn( "Overwriting released artifacts in repository '{}' is not allowed.",
655                                           managedRepositoryContent.getId() );
656                                 throw new DavException( HttpServletResponse.SC_CONFLICT,
657                                                         "Overwriting released artifacts is not allowed." );
658                             }
659                         }
660                     }
661                     catch ( LayoutException e )
662                     {
663                         log.warn( "Artifact path '{}' is invalid.", resourcePath );
664                     }
665                 }
666
667                 /*
668                  * Create parent directories that don't exist when writing a file This actually makes this
669                  * implementation not compliant to the WebDAV RFC - but we have enough knowledge about how the
670                  * collection is being used to do this reasonably and some versions of Maven's WebDAV don't correctly
671                  * create the collections themselves.
672                  */
673
674                 File rootDirectory = new File( managedRepositoryContent.getRepoRoot() );
675                 File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile();
676
677                 if ( !destDir.exists() )
678                 {
679                     destDir.mkdirs();
680                     String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
681
682                     log.debug( "Creating destination directory '{}' (current user '{}')", destDir.getName(),
683                                activePrincipal );
684
685                     triggerAuditEvent( request.getRemoteAddr(), managedRepositoryContent.getId(), relPath,
686                                        AuditEvent.CREATE_DIR, activePrincipal );
687                 }
688             }
689         }
690         return resource;
691     }
692
693     public DavResource createResource( final DavResourceLocator locator, final DavSession davSession )
694         throws DavException
695     {
696         ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator );
697
698         ManagedRepositoryContent managedRepositoryContent;
699         try
700         {
701             managedRepositoryContent =
702                 repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() );
703         }
704         catch ( RepositoryNotFoundException e )
705         {
706             throw new DavException( HttpServletResponse.SC_NOT_FOUND,
707                                     "Invalid repository: " + archivaLocator.getRepositoryId() );
708         }
709         catch ( RepositoryException e )
710         {
711             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
712         }
713
714         DavResource resource = null;
715         try
716         {
717             String logicalResource = getLogicalResource( archivaLocator, managedRepositoryAdmin.getManagedRepository(
718                 archivaLocator.getRepositoryId() ), false );
719             if ( logicalResource.startsWith( "/" ) )
720             {
721                 logicalResource = logicalResource.substring( 1 );
722             }
723             File resourceFile = new File( managedRepositoryContent.getRepoRoot(), logicalResource );
724             resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource,
725                                                managedRepositoryContent.getRepository(), davSession, archivaLocator,
726                                                this, mimeTypes, auditListeners, scheduler );
727
728             resource.addLockManager( lockManager );
729         }
730         catch ( RepositoryAdminException e )
731         {
732             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
733         }
734         return resource;
735     }
736
737     private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request,
738                                              LogicalResource resource )
739         throws DavException
740     {
741         String path = resource.getPath();
742         if ( repositoryRequest.isSupportFile( path ) )
743         {
744             File proxiedFile = connectors.fetchFromProxies( managedRepository, path );
745
746             return ( proxiedFile != null );
747         }
748
749         // Is it a Metadata resource?
750         if ( repositoryRequest.isDefault( path ) && repositoryRequest.isMetadata( path ) )
751         {
752             return connectors.fetchMetatadaFromProxies( managedRepository, path ) != null;
753         }
754
755         // Is it an Archetype Catalog?
756         if ( repositoryRequest.isArchetypeCatalog( path ) )
757         {
758             // FIXME we must implement a merge of remote archetype catalog from remote servers.
759             File proxiedFile = connectors.fetchFromProxies( managedRepository, path );
760
761             return ( proxiedFile != null );
762         }
763
764         // Not any of the above? Then it's gotta be an artifact reference.
765         try
766         {
767             // Get the artifact reference in a layout neutral way.
768             ArtifactReference artifact = repositoryRequest.toArtifactReference( path );
769
770             if ( artifact != null )
771             {
772                 String repositoryLayout = managedRepository.getRepository().getLayout();
773
774                 RepositoryStorage repositoryStorage =
775                     this.applicationContext.getBean( "repositoryStorage#" + repositoryLayout, RepositoryStorage.class );
776                 repositoryStorage.applyServerSideRelocation( managedRepository, artifact );
777
778                 File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
779
780                 resource.setPath( managedRepository.toPath( artifact ) );
781
782                 log.debug( "Proxied artifact '{}:{}:{}'", artifact.getGroupId(), artifact.getArtifactId(),
783                            artifact.getVersion() );
784
785                 return ( proxiedFile != null );
786             }
787         }
788         catch ( LayoutException e )
789         {
790             /* eat it */
791         }
792         catch ( ProxyDownloadException e )
793         {
794             log.error( e.getMessage(), e );
795             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
796                                     "Unable to fetch artifact resource." );
797         }
798         return false;
799     }
800
801     // TODO: remove?
802
803     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action,
804                                     String principal )
805     {
806         AuditEvent event = new AuditEvent( repositoryId, principal, resource, action );
807         event.setRemoteIP( remoteIP );
808
809         for ( AuditListener listener : auditListeners )
810         {
811             listener.auditEvent( event );
812         }
813     }
814
815     public void addAuditListener( AuditListener listener )
816     {
817         this.auditListeners.add( listener );
818     }
819
820     public void clearAuditListeners()
821     {
822         this.auditListeners.clear();
823     }
824
825     public void removeAuditListener( AuditListener listener )
826     {
827         this.auditListeners.remove( listener );
828     }
829
830     private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource )
831     {
832         // [MRM-503] - Metadata file need Pragma:no-cache response
833         // header.
834         if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" )
835             || ( (ArchivaDavResource) resource ).getLocalResource().isDirectory() )
836         {
837             response.setHeader( "Pragma", "no-cache" );
838             response.setHeader( "Cache-Control", "no-cache" );
839             response.setDateHeader( "Last-Modified", new Date().getTime() );
840         }
841         // if the resource is a directory don't cache it as new groupId deployed will be available
842         // without need of refreshing browser
843         else
844         {
845             // We need to specify this so connecting wagons can work correctly
846             response.setDateHeader( "Last-Modified", resource.getModificationTime() );
847         }
848         // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
849     }
850
851     private ArchivaDavResourceLocator checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator )
852         throws DavException
853     {
854         if ( !( locator instanceof ArchivaDavResourceLocator ) )
855         {
856             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
857                                     "Locator does not implement RepositoryLocator" );
858         }
859
860         // Hidden paths
861         if ( locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
862         {
863             throw new DavException( HttpServletResponse.SC_NOT_FOUND );
864         }
865
866         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
867
868         // MRM-419 - Windows Webdav support. Should not 404 if there is no content.
869         if ( StringUtils.isEmpty( archivaLocator.getRepositoryId() ) )
870         {
871             throw new DavException( HttpServletResponse.SC_NO_CONTENT );
872         }
873         return archivaLocator;
874     }
875
876     private static class LogicalResource
877     {
878         private String path;
879
880         public LogicalResource( String path )
881         {
882             this.path = path;
883         }
884
885         public String getPath()
886         {
887             return path;
888         }
889
890         public void setPath( String path )
891         {
892             this.path = path;
893         }
894     }
895
896     protected boolean isAuthorized( DavServletRequest request, String repositoryId )
897         throws DavException
898     {
899         try
900         {
901             AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
902             SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) );
903
904             return servletAuth.isAuthenticated( request, result ) && servletAuth.isAuthorized( request, securitySession,
905                                                                                                repositoryId,
906                                                                                                WebdavMethodUtil.getMethodPermission(
907                                                                                                    request.getMethod() ) );
908         }
909         catch ( AuthenticationException e )
910         {
911             // safety check for MRM-911
912             String guest = UserManager.GUEST_USERNAME;
913             try
914             {
915                 if ( servletAuth.isAuthorized( guest,
916                                                ( (ArchivaDavResourceLocator) request.getRequestLocator() ).getRepositoryId(),
917                                                WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
918                 {
919                     return true;
920                 }
921             }
922             catch ( UnauthorizedException ae )
923             {
924                 throw new UnauthorizedDavException( repositoryId,
925                                                     "You are not authenticated and authorized to access any repository." );
926             }
927
928             throw new UnauthorizedDavException( repositoryId, "You are not authenticated" );
929         }
930         catch ( MustChangePasswordException e )
931         {
932             throw new UnauthorizedDavException( repositoryId, "You must change your password." );
933         }
934         catch ( AccountLockedException e )
935         {
936             throw new UnauthorizedDavException( repositoryId, "User account is locked." );
937         }
938         catch ( AuthorizationException e )
939         {
940             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
941                                     "Fatal Authorization Subsystem Error." );
942         }
943         catch ( UnauthorizedException e )
944         {
945             throw new UnauthorizedDavException( repositoryId, e.getMessage() );
946         }
947     }
948
949     private DavResource getResourceFromGroup( DavServletRequest request, List<String> repositories,
950                                               ArchivaDavResourceLocator locator,
951                                               RepositoryGroupConfiguration repositoryGroupConfiguration )
952         throws DavException, RepositoryAdminException
953     {
954         List<File> mergedRepositoryContents = new ArrayList<File>();
955         // multiple repo types so we guess they are all the same type
956         // so use the first one
957         // FIXME add a method with group in the repository storage
958         String firstRepoId = repositoryGroupConfiguration.getRepositories().get( 1 );
959
960         String path = getLogicalResource( locator, managedRepositoryAdmin.getManagedRepository( firstRepoId ), false );
961         if ( path.startsWith( "/" ) )
962         {
963             path = path.substring( 1 );
964         }
965         LogicalResource logicalResource = new LogicalResource( path );
966
967         // flow:
968         // if the current user logged in has permission to any of the repositories, allow user to
969         // browse the repo group but displaying only the repositories which the user has permission to access.
970         // otherwise, prompt for authentication.
971
972         String activePrincipal = getActivePrincipal( request );
973
974         boolean allow = isAllowedToContinue( request, repositories, activePrincipal );
975
976         // remove last /
977         String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" );
978
979         if ( allow )
980         {
981
982             if ( StringUtils.endsWith( pathInfo, repositoryGroupConfiguration.getMergedIndexPath() ) )
983             {
984                 File mergedRepoDir =
985                     buildMergedIndexDirectory( repositories, activePrincipal, request, repositoryGroupConfiguration );
986                 mergedRepositoryContents.add( mergedRepoDir );
987             }
988             else
989             {
990                 if ( StringUtils.equalsIgnoreCase( pathInfo, "/" + repositoryGroupConfiguration.getId() ) )
991                 {
992                     File tmpDirectory = new File( SystemUtils.getJavaIoTmpDir(),
993                                                   repositoryGroupConfiguration.getId() + "/"
994                                                       + repositoryGroupConfiguration.getMergedIndexPath() );
995                     if ( !tmpDirectory.exists() )
996                     {
997                         synchronized ( tmpDirectory.getAbsolutePath() )
998                         {
999                             if ( !tmpDirectory.exists() )
1000                             {
1001                                 tmpDirectory.mkdirs();
1002                             }
1003                         }
1004                     }
1005                     mergedRepositoryContents.add( tmpDirectory.getParentFile() );
1006                 }
1007                 for ( String repository : repositories )
1008                 {
1009                     ManagedRepositoryContent managedRepository = null;
1010
1011                     try
1012                     {
1013                         managedRepository = repositoryFactory.getManagedRepositoryContent( repository );
1014                     }
1015                     catch ( RepositoryNotFoundException e )
1016                     {
1017                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1018                                                 "Invalid managed repository <" + repository + ">: " + e.getMessage() );
1019                     }
1020                     catch ( RepositoryException e )
1021                     {
1022                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1023                                                 "Invalid managed repository <" + repository + ">: " + e.getMessage() );
1024                     }
1025
1026                     File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
1027                     if ( resourceFile.exists() )
1028                     {
1029                         // in case of group displaying index directory doesn't have sense !!
1030                         String repoIndexDirectory = managedRepository.getRepository().getIndexDirectory();
1031                         if ( StringUtils.isNotEmpty( repoIndexDirectory ) )
1032                         {
1033                             if ( !new File( repoIndexDirectory ).isAbsolute() )
1034                             {
1035                                 repoIndexDirectory = new File( managedRepository.getRepository().getLocation(),
1036                                                                StringUtils.isEmpty( repoIndexDirectory )
1037                                                                    ? ".indexer"
1038                                                                    : repoIndexDirectory ).getAbsolutePath();
1039                             }
1040                         }
1041                         if ( StringUtils.isEmpty( repoIndexDirectory ) )
1042                         {
1043                             repoIndexDirectory = new File( managedRepository.getRepository().getLocation(),
1044                                                            ".indexer" ).getAbsolutePath();
1045                         }
1046
1047                         if ( !StringUtils.equals( FilenameUtils.normalize( repoIndexDirectory ),
1048                                                   FilenameUtils.normalize( resourceFile.getAbsolutePath() ) ) )
1049                         {
1050                             // for prompted authentication
1051                             if ( httpAuth.getSecuritySession( request.getSession( true ) ) != null )
1052                             {
1053                                 try
1054                                 {
1055                                     if ( isAuthorized( request, repository ) )
1056                                     {
1057                                         mergedRepositoryContents.add( resourceFile );
1058                                         log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal );
1059                                     }
1060                                 }
1061                                 catch ( DavException e )
1062                                 {
1063                                     // TODO: review exception handling
1064
1065                                     log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository,
1066                                                activePrincipal, e.getMessage() );
1067
1068                                 }
1069
1070                             }
1071                             else
1072                             {
1073                                 // for the current user logged in
1074                                 try
1075                                 {
1076                                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1077                                                                    WebdavMethodUtil.getMethodPermission(
1078                                                                        request.getMethod() ) ) )
1079                                     {
1080                                         mergedRepositoryContents.add( resourceFile );
1081                                         log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal );
1082                                     }
1083                                 }
1084                                 catch ( UnauthorizedException e )
1085                                 {
1086                                     // TODO: review exception handling
1087
1088                                     log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository,
1089                                                activePrincipal, e.getMessage() );
1090
1091                                 }
1092                             }
1093                         }
1094                     }
1095                 }
1096             }
1097         }
1098         else
1099         {
1100             throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
1101         }
1102
1103         ArchivaVirtualDavResource resource =
1104             new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator,
1105                                            this );
1106
1107         // compatibility with MRM-440 to ensure browsing the repository group works ok
1108         if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) )
1109         {
1110             throw new BrowserRedirectException( resource.getHref() );
1111         }
1112
1113         return resource;
1114     }
1115
1116     protected String getActivePrincipal( DavServletRequest request )
1117     {
1118         User sessionUser = httpAuth.getSessionUser( request.getSession() );
1119         return sessionUser != null ? sessionUser.getUsername() : UserManager.GUEST_USERNAME;
1120     }
1121
1122     /**
1123      * Check if the current user is authorized to access any of the repos
1124      *
1125      * @param request
1126      * @param repositories
1127      * @param activePrincipal
1128      * @return
1129      */
1130     private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal )
1131     {
1132         // when no repositories configured it's impossible to browse nothing !
1133         // at least make possible to see nothing :-)
1134         if ( repositories == null || repositories.isEmpty() )
1135         {
1136             return true;
1137         }
1138
1139         boolean allow = false;
1140
1141         // if securitySession != null, it means that the user was prompted for authentication
1142         if ( httpAuth.getSecuritySession( request.getSession() ) != null )
1143         {
1144             for ( String repository : repositories )
1145             {
1146                 try
1147                 {
1148                     if ( isAuthorized( request, repository ) )
1149                     {
1150                         allow = true;
1151                         break;
1152                     }
1153                 }
1154                 catch ( DavException e )
1155                 {
1156                     continue;
1157                 }
1158             }
1159         }
1160         else
1161         {
1162             for ( String repository : repositories )
1163             {
1164                 try
1165                 {
1166                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1167                                                    WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
1168                     {
1169                         allow = true;
1170                         break;
1171                     }
1172                 }
1173                 catch ( UnauthorizedException e )
1174                 {
1175                     continue;
1176                 }
1177             }
1178         }
1179
1180         return allow;
1181     }
1182
1183     private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename )
1184         throws RepositoryMetadataException, DigesterException, IOException
1185     {
1186         File outputFile = new File( outputFilename );
1187         if ( outputFile.exists() )
1188         {
1189             FileUtils.deleteQuietly( outputFile );
1190         }
1191
1192         outputFile.getParentFile().mkdirs();
1193         RepositoryMetadataWriter.write( mergedMetadata, outputFile );
1194
1195         createChecksumFile( outputFilename, digestSha1 );
1196         createChecksumFile( outputFilename, digestMd5 );
1197
1198         return outputFile;
1199     }
1200
1201     private void createChecksumFile( String path, Digester digester )
1202         throws DigesterException, IOException
1203     {
1204         File checksumFile = new File( path + digester.getFilenameExtension() );
1205         if ( !checksumFile.exists() )
1206         {
1207             FileUtils.deleteQuietly( checksumFile );
1208             checksum.createChecksum( new File( path ), digester );
1209         }
1210         else if ( !checksumFile.isFile() )
1211         {
1212             log.error( "Checksum file is not a file." );
1213         }
1214     }
1215
1216     private boolean isProjectReference( String requestedResource )
1217     {
1218         try
1219         {
1220             metadataTools.toVersionedReference( requestedResource );
1221             return false;
1222         }
1223         catch ( RepositoryMetadataException re )
1224         {
1225             return true;
1226         }
1227     }
1228
1229     protected File buildMergedIndexDirectory( List<String> repositories, String activePrincipal,
1230                                               DavServletRequest request,
1231                                               RepositoryGroupConfiguration repositoryGroupConfiguration )
1232         throws DavException
1233     {
1234
1235         try
1236         {
1237             HttpSession session = request.getSession();
1238
1239             Map<String, TemporaryGroupIndex> temporaryGroupIndexMap =
1240                 (Map<String, TemporaryGroupIndex>) session.getAttribute(
1241                     TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY );
1242             if ( temporaryGroupIndexMap == null )
1243             {
1244                 temporaryGroupIndexMap = new HashMap<String, TemporaryGroupIndex>();
1245             }
1246
1247             TemporaryGroupIndex tmp = temporaryGroupIndexMap.get( repositoryGroupConfiguration.getId() );
1248
1249             if ( tmp != null && tmp.getDirectory() != null && tmp.getDirectory().exists() )
1250             {
1251                 if ( System.currentTimeMillis() - tmp.getCreationTime() > (
1252                     repositoryGroupConfiguration.getMergedIndexTtl() * 60 * 1000 ) )
1253                 {
1254                     log.debug( MarkerFactory.getMarker( "group.merged.index" ),
1255                                "tmp group index '{}' is too old so delete it", repositoryGroupConfiguration.getId() );
1256                     indexMerger.cleanTemporaryGroupIndex( tmp );
1257                 }
1258                 else
1259                 {
1260                     log.debug( MarkerFactory.getMarker( "group.merged.index" ),
1261                                "merged index for group '{}' found in cache", repositoryGroupConfiguration.getId() );
1262                     return tmp.getDirectory();
1263                 }
1264             }
1265
1266             Set<String> authzRepos = new HashSet<String>();
1267             for ( String repository : repositories )
1268             {
1269                 try
1270                 {
1271                     if ( servletAuth.isAuthorized( activePrincipal, repository,
1272                                                    WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) )
1273                     {
1274                         authzRepos.add( repository );
1275                         authzRepos.addAll( this.repositorySearch.getRemoteIndexingContextIds( repository ) );
1276                     }
1277                 }
1278                 catch ( UnauthorizedException e )
1279                 {
1280                     // TODO: review exception handling
1281
1282                     log.debug( "Skipping repository '{}' for user '{}': {}", repository, activePrincipal,
1283                                e.getMessage() );
1284                 }
1285             }
1286             log.info( "generate temporary merged index for repository group '{}' for repositories '{}'",
1287                       repositoryGroupConfiguration.getId(), authzRepos );
1288             IndexingContext indexingContext = indexMerger.buildMergedIndex(
1289                 new IndexMergerRequest( authzRepos, true, repositoryGroupConfiguration.getId(),
1290                                         repositoryGroupConfiguration.getMergedIndexPath(),
1291                                         repositoryGroupConfiguration.getMergedIndexTtl() ) );
1292             File mergedRepoDir = indexingContext.getIndexDirectoryFile();
1293             TemporaryGroupIndex temporaryGroupIndex =
1294                 new TemporaryGroupIndex( mergedRepoDir, indexingContext.getId(), repositoryGroupConfiguration.getId(),
1295                                          repositoryGroupConfiguration.getMergedIndexTtl() ).setCreationTime(
1296                     new Date().getTime() );
1297             temporaryGroupIndexMap.put( repositoryGroupConfiguration.getId(), temporaryGroupIndex );
1298             session.setAttribute( TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY,
1299                                   temporaryGroupIndexMap );
1300             return mergedRepoDir;
1301         }
1302         catch ( RepositoryAdminException e )
1303         {
1304             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
1305         }
1306         catch ( IndexMergerException e )
1307         {
1308             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e );
1309         }
1310     }
1311
1312
1313     public void setServletAuth( ServletAuthenticator servletAuth )
1314     {
1315         this.servletAuth = servletAuth;
1316     }
1317
1318     public void setHttpAuth( HttpAuthenticator httpAuth )
1319     {
1320         this.httpAuth = httpAuth;
1321     }
1322
1323     public void setScheduler( RepositoryArchivaTaskScheduler scheduler )
1324     {
1325         this.scheduler = scheduler;
1326     }
1327
1328     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1329     {
1330         this.archivaConfiguration = archivaConfiguration;
1331     }
1332
1333     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1334     {
1335         this.repositoryFactory = repositoryFactory;
1336     }
1337
1338     public void setRepositoryRequest( RepositoryRequest repositoryRequest )
1339     {
1340         this.repositoryRequest = repositoryRequest;
1341     }
1342
1343     public void setConnectors( RepositoryProxyConnectors connectors )
1344     {
1345         this.connectors = connectors;
1346     }
1347
1348     public RemoteRepositoryAdmin getRemoteRepositoryAdmin()
1349     {
1350         return remoteRepositoryAdmin;
1351     }
1352
1353     public void setRemoteRepositoryAdmin( RemoteRepositoryAdmin remoteRepositoryAdmin )
1354     {
1355         this.remoteRepositoryAdmin = remoteRepositoryAdmin;
1356     }
1357
1358     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
1359     {
1360         return managedRepositoryAdmin;
1361     }
1362
1363     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
1364     {
1365         this.managedRepositoryAdmin = managedRepositoryAdmin;
1366     }
1367 }