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