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