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