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