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